mirror of https://github.com/OpenRCT2/OpenRCT2.git
Merge pull request #14561 from IntelOrca/increase-paint-structs
Dynamically allocate paint structs and remove 4000 per column limit
This commit is contained in:
commit
14845b61e5
|
@ -34,47 +34,50 @@
|
||||||
# include <iterator>
|
# include <iterator>
|
||||||
# include <vector>
|
# include <vector>
|
||||||
|
|
||||||
static void fixup_pointers(paint_session* s, size_t paint_session_entries, size_t paint_struct_entries, size_t quadrant_entries)
|
static void fixup_pointers(std::vector<RecordedPaintSession>& s)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < paint_session_entries; i++)
|
for (size_t i = 0; i < s.size(); i++)
|
||||||
{
|
{
|
||||||
for (size_t j = 0; j < paint_struct_entries; j++)
|
auto& entries = s[i].Entries;
|
||||||
|
auto& quadrants = s[i].Session.Quadrants;
|
||||||
|
for (size_t j = 0; j < entries.size(); j++)
|
||||||
{
|
{
|
||||||
if (s[i].PaintStructs[j].basic.next_quadrant_ps == reinterpret_cast<paint_struct*>(paint_struct_entries))
|
if (entries[j].basic.next_quadrant_ps == reinterpret_cast<paint_struct*>(-1))
|
||||||
{
|
{
|
||||||
s[i].PaintStructs[j].basic.next_quadrant_ps = nullptr;
|
entries[j].basic.next_quadrant_ps = nullptr;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto nextQuadrantPs = reinterpret_cast<uintptr_t>(s[i].PaintStructs[j].basic.next_quadrant_ps);
|
auto nextQuadrantPs = reinterpret_cast<size_t>(entries[j].basic.next_quadrant_ps) / sizeof(paint_entry);
|
||||||
s[i].PaintStructs[j].basic.next_quadrant_ps = &s[i].PaintStructs[nextQuadrantPs].basic;
|
entries[j].basic.next_quadrant_ps = &s[i].Entries[nextQuadrantPs].basic;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (size_t j = 0; j < quadrant_entries; j++)
|
for (size_t j = 0; j < std::size(quadrants); j++)
|
||||||
{
|
{
|
||||||
if (s[i].Quadrants[j] == reinterpret_cast<paint_struct*>(quadrant_entries))
|
if (quadrants[j] == reinterpret_cast<paint_struct*>(-1))
|
||||||
{
|
{
|
||||||
s[i].Quadrants[j] = nullptr;
|
quadrants[j] = nullptr;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
s[i].Quadrants[j] = &s[i].PaintStructs[reinterpret_cast<size_t>(s[i].Quadrants[j])].basic;
|
auto ps = reinterpret_cast<size_t>(quadrants[j]) / sizeof(paint_entry);
|
||||||
|
quadrants[j] = &entries[ps].basic;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::vector<paint_session> extract_paint_session(const std::string parkFileName)
|
static std::vector<RecordedPaintSession> extract_paint_session(std::string_view parkFileName)
|
||||||
{
|
{
|
||||||
core_init();
|
core_init();
|
||||||
gOpenRCT2Headless = true;
|
gOpenRCT2Headless = true;
|
||||||
auto context = OpenRCT2::CreateContext();
|
auto context = OpenRCT2::CreateContext();
|
||||||
std::vector<paint_session> sessions;
|
std::vector<RecordedPaintSession> sessions;
|
||||||
log_info("Starting...");
|
log_info("Starting...");
|
||||||
if (context->Initialise())
|
if (context->Initialise())
|
||||||
{
|
{
|
||||||
drawing_engine_init();
|
drawing_engine_init();
|
||||||
if (!context->LoadParkFromFile(parkFileName))
|
if (!context->LoadParkFromFile(std::string(parkFileName)))
|
||||||
{
|
{
|
||||||
log_error("Failed to load park!");
|
log_error("Failed to load park!");
|
||||||
return {};
|
return {};
|
||||||
|
@ -133,21 +136,21 @@ static std::vector<paint_session> extract_paint_session(const std::string parkFi
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function is based on benchgfx_render_screenshots
|
// This function is based on benchgfx_render_screenshots
|
||||||
static void BM_paint_session_arrange(benchmark::State& state, const std::vector<paint_session> inputSessions)
|
static void BM_paint_session_arrange(benchmark::State& state, const std::vector<RecordedPaintSession> inputSessions)
|
||||||
{
|
{
|
||||||
std::vector<paint_session> sessions = inputSessions;
|
auto sessions = inputSessions;
|
||||||
// Fixing up the pointers continuously is wasteful. Fix it up once for `sessions` and store a copy.
|
// Fixing up the pointers continuously is wasteful. Fix it up once for `sessions` and store a copy.
|
||||||
// Keep in mind we need bit-exact copy, as the lists use pointers.
|
// Keep in mind we need bit-exact copy, as the lists use pointers.
|
||||||
// Once sorted, just restore the copy with the original fixed-up version.
|
// Once sorted, just restore the copy with the original fixed-up version.
|
||||||
paint_session* local_s = new paint_session[std::size(sessions)];
|
RecordedPaintSession* local_s = new RecordedPaintSession[std::size(sessions)];
|
||||||
fixup_pointers(&sessions[0], std::size(sessions), std::size(local_s->PaintStructs), std::size(local_s->Quadrants));
|
fixup_pointers(sessions);
|
||||||
std::copy_n(sessions.cbegin(), std::size(sessions), local_s);
|
std::copy_n(sessions.cbegin(), std::size(sessions), local_s);
|
||||||
for (auto _ : state)
|
for (auto _ : state)
|
||||||
{
|
{
|
||||||
state.PauseTiming();
|
state.PauseTiming();
|
||||||
std::copy_n(local_s, std::size(sessions), sessions.begin());
|
std::copy_n(local_s, std::size(sessions), sessions.begin());
|
||||||
state.ResumeTiming();
|
state.ResumeTiming();
|
||||||
PaintSessionArrange(&sessions[0]);
|
PaintSessionArrange(&sessions[0].Session);
|
||||||
benchmark::DoNotOptimize(sessions);
|
benchmark::DoNotOptimize(sessions);
|
||||||
}
|
}
|
||||||
state.SetItemsProcessed(state.iterations() * std::size(sessions));
|
state.SetItemsProcessed(state.iterations() * std::size(sessions));
|
||||||
|
@ -158,14 +161,14 @@ static int cmdline_for_bench_sprite_sort(int argc, const char** argv)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
// Register some basic "baseline" benchmark
|
// Register some basic "baseline" benchmark
|
||||||
std::vector<paint_session> sessions(1);
|
std::vector<RecordedPaintSession> sessions(1);
|
||||||
for (auto& ps : sessions[0].PaintStructs)
|
for (auto& ps : sessions[0].Entries)
|
||||||
{
|
{
|
||||||
ps.basic.next_quadrant_ps = reinterpret_cast<paint_struct*>((std::size(sessions[0].PaintStructs)));
|
ps.basic.next_quadrant_ps = reinterpret_cast<paint_struct*>(-1);
|
||||||
}
|
}
|
||||||
for (auto& quad : sessions[0].Quadrants)
|
for (auto& quad : sessions[0].Session.Quadrants)
|
||||||
{
|
{
|
||||||
quad = reinterpret_cast<paint_struct*>((std::size(sessions[0].Quadrants)));
|
quad = reinterpret_cast<paint_struct*>(-1);
|
||||||
}
|
}
|
||||||
benchmark::RegisterBenchmark("baseline", BM_paint_session_arrange, sessions);
|
benchmark::RegisterBenchmark("baseline", BM_paint_session_arrange, sessions);
|
||||||
}
|
}
|
||||||
|
@ -183,7 +186,7 @@ static int cmdline_for_bench_sprite_sort(int argc, const char** argv)
|
||||||
if (Platform::FileExists(argv[i]))
|
if (Platform::FileExists(argv[i]))
|
||||||
{
|
{
|
||||||
// Register benchmark for sv6 if valid
|
// Register benchmark for sv6 if valid
|
||||||
std::vector<paint_session> sessions = extract_paint_session(argv[i]);
|
std::vector<RecordedPaintSession> sessions = extract_paint_session(argv[i]);
|
||||||
if (!sessions.empty())
|
if (!sessions.empty())
|
||||||
benchmark::RegisterBenchmark(argv[i], BM_paint_session_arrange, sessions);
|
benchmark::RegisterBenchmark(argv[i], BM_paint_session_arrange, sessions);
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <list>
|
#include <list>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
using namespace OpenRCT2;
|
using namespace OpenRCT2;
|
||||||
|
|
||||||
|
@ -805,7 +806,7 @@ void viewport_update_smart_vehicle_follow(rct_window* window)
|
||||||
*/
|
*/
|
||||||
void viewport_render(
|
void viewport_render(
|
||||||
rct_drawpixelinfo* dpi, const rct_viewport* viewport, int32_t left, int32_t top, int32_t right, int32_t bottom,
|
rct_drawpixelinfo* dpi, const rct_viewport* viewport, int32_t left, int32_t top, int32_t right, int32_t bottom,
|
||||||
std::vector<paint_session>* sessions)
|
std::vector<RecordedPaintSession>* sessions)
|
||||||
{
|
{
|
||||||
if (right <= viewport->pos.x)
|
if (right <= viewport->pos.x)
|
||||||
return;
|
return;
|
||||||
|
@ -846,30 +847,68 @@ void viewport_render(
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static void record_session(const paint_session* session, std::vector<paint_session>* recorded_sessions, size_t record_index)
|
static void record_session(
|
||||||
|
const paint_session* session, std::vector<RecordedPaintSession>* recorded_sessions, size_t record_index)
|
||||||
{
|
{
|
||||||
// Perform a deep copy of the paint session, use relative offsets.
|
// Perform a deep copy of the paint session, use relative offsets.
|
||||||
// This is done to extract the session for benchmark.
|
// This is done to extract the session for benchmark.
|
||||||
// Place the copied session at provided record_index, so the caller can decide which columns/paint sessions to copy;
|
// Place the copied session at provided record_index, so the caller can decide which columns/paint sessions to copy;
|
||||||
// there is no column information embedded in the session itself.
|
// there is no column information embedded in the session itself.
|
||||||
(*recorded_sessions)[record_index] = (*session);
|
auto& recordedSession = recorded_sessions->at(record_index);
|
||||||
paint_session* session_copy = &recorded_sessions->at(record_index);
|
recordedSession.Session = *session;
|
||||||
|
recordedSession.Entries.resize(session->PaintEntryChain.GetCount());
|
||||||
|
|
||||||
// Mind the offset needs to be calculated against the original `session`, not `session_copy`
|
// Mind the offset needs to be calculated against the original `session`, not `session_copy`
|
||||||
for (auto& ps : session_copy->PaintStructs)
|
std::unordered_map<paint_struct*, paint_struct*> entryRemap;
|
||||||
|
|
||||||
|
// Copy all entries
|
||||||
|
auto paintIndex = 0;
|
||||||
|
auto chain = session->PaintEntryChain.Head;
|
||||||
|
while (chain != nullptr)
|
||||||
{
|
{
|
||||||
ps.basic.next_quadrant_ps = reinterpret_cast<paint_struct*>(
|
for (size_t i = 0; i < chain->Count; i++)
|
||||||
ps.basic.next_quadrant_ps ? int(ps.basic.next_quadrant_ps - &session->PaintStructs[0].basic)
|
{
|
||||||
: std::size(session->PaintStructs));
|
auto& src = chain->PaintStructs[i];
|
||||||
|
auto& dst = recordedSession.Entries[paintIndex++];
|
||||||
|
dst = src;
|
||||||
|
entryRemap[&src.basic] = reinterpret_cast<paint_struct*>(i * sizeof(paint_entry));
|
||||||
|
}
|
||||||
|
chain = chain->Next;
|
||||||
}
|
}
|
||||||
for (auto& quad : session_copy->Quadrants)
|
entryRemap[nullptr] = reinterpret_cast<paint_struct*>(-1);
|
||||||
|
|
||||||
|
// Remap all entries
|
||||||
|
for (auto& ps : recordedSession.Entries)
|
||||||
{
|
{
|
||||||
quad = reinterpret_cast<paint_struct*>(
|
auto& ptr = ps.basic.next_quadrant_ps;
|
||||||
quad ? int(quad - &session->PaintStructs[0].basic) : std::size(session->Quadrants));
|
auto it = entryRemap.find(ptr);
|
||||||
|
if (it == entryRemap.end())
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
ptr = nullptr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ptr = it->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto& ptr : recordedSession.Session.Quadrants)
|
||||||
|
{
|
||||||
|
auto it = entryRemap.find(ptr);
|
||||||
|
if (it == entryRemap.end())
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
ptr = nullptr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ptr = it->second;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void viewport_fill_column(paint_session* session, std::vector<paint_session>* recorded_sessions, size_t record_index)
|
static void viewport_fill_column(
|
||||||
|
paint_session* session, std::vector<RecordedPaintSession>* recorded_sessions, size_t record_index)
|
||||||
{
|
{
|
||||||
PaintSessionGenerate(session);
|
PaintSessionGenerate(session);
|
||||||
if (recorded_sessions != nullptr)
|
if (recorded_sessions != nullptr)
|
||||||
|
@ -922,7 +961,7 @@ static void viewport_paint_column(paint_session* session)
|
||||||
*/
|
*/
|
||||||
void viewport_paint(
|
void viewport_paint(
|
||||||
const rct_viewport* viewport, rct_drawpixelinfo* dpi, int16_t left, int16_t top, int16_t right, int16_t bottom,
|
const rct_viewport* viewport, rct_drawpixelinfo* dpi, int16_t left, int16_t top, int16_t right, int16_t bottom,
|
||||||
std::vector<paint_session>* recorded_sessions)
|
std::vector<RecordedPaintSession>* recorded_sessions)
|
||||||
{
|
{
|
||||||
uint32_t viewFlags = viewport->flags;
|
uint32_t viewFlags = viewport->flags;
|
||||||
uint16_t width = right - left;
|
uint16_t width = right - left;
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
struct paint_session;
|
struct paint_session;
|
||||||
|
struct RecordedPaintSession;
|
||||||
struct paint_struct;
|
struct paint_struct;
|
||||||
struct rct_drawpixelinfo;
|
struct rct_drawpixelinfo;
|
||||||
struct Peep;
|
struct Peep;
|
||||||
|
@ -116,10 +117,10 @@ void viewport_update_smart_staff_follow(rct_window* window, Peep* peep);
|
||||||
void viewport_update_smart_vehicle_follow(rct_window* window);
|
void viewport_update_smart_vehicle_follow(rct_window* window);
|
||||||
void viewport_render(
|
void viewport_render(
|
||||||
rct_drawpixelinfo* dpi, const rct_viewport* viewport, int32_t left, int32_t top, int32_t right, int32_t bottom,
|
rct_drawpixelinfo* dpi, const rct_viewport* viewport, int32_t left, int32_t top, int32_t right, int32_t bottom,
|
||||||
std::vector<paint_session>* sessions = nullptr);
|
std::vector<RecordedPaintSession>* sessions = nullptr);
|
||||||
void viewport_paint(
|
void viewport_paint(
|
||||||
const rct_viewport* viewport, rct_drawpixelinfo* dpi, int16_t left, int16_t top, int16_t right, int16_t bottom,
|
const rct_viewport* viewport, rct_drawpixelinfo* dpi, int16_t left, int16_t top, int16_t right, int16_t bottom,
|
||||||
std::vector<paint_session>* sessions = nullptr);
|
std::vector<RecordedPaintSession>* sessions = nullptr);
|
||||||
|
|
||||||
CoordsXYZ viewport_adjust_for_map_height(const ScreenCoordsXY& startCoords);
|
CoordsXYZ viewport_adjust_for_map_height(const ScreenCoordsXY& startCoords);
|
||||||
|
|
||||||
|
|
|
@ -139,9 +139,6 @@ static paint_struct* CreateNormalPaintStruct(
|
||||||
paint_session* session, const uint32_t image_id, const CoordsXYZ& offset, const CoordsXYZ& boundBoxSize,
|
paint_session* session, const uint32_t image_id, const CoordsXYZ& offset, const CoordsXYZ& boundBoxSize,
|
||||||
const CoordsXYZ& boundBoxOffset)
|
const CoordsXYZ& boundBoxOffset)
|
||||||
{
|
{
|
||||||
if (session->NoPaintStructsAvailable())
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
auto* const g1 = gfx_get_g1_element(image_id & 0x7FFFF);
|
auto* const g1 = gfx_get_g1_element(image_id & 0x7FFFF);
|
||||||
if (g1 == nullptr)
|
if (g1 == nullptr)
|
||||||
{
|
{
|
||||||
|
@ -162,7 +159,12 @@ static paint_struct* CreateNormalPaintStruct(
|
||||||
const auto rotBoundBoxOffset = CoordsXYZ{ boundBoxOffset.Rotate(swappedRotation), boundBoxOffset.z };
|
const auto rotBoundBoxOffset = CoordsXYZ{ boundBoxOffset.Rotate(swappedRotation), boundBoxOffset.z };
|
||||||
const auto rotBoundBoxSize = RotateBoundBoxSize(boundBoxSize, session->CurrentRotation);
|
const auto rotBoundBoxSize = RotateBoundBoxSize(boundBoxSize, session->CurrentRotation);
|
||||||
|
|
||||||
paint_struct* ps = session->AllocateNormalPaintEntry();
|
auto* ps = session->AllocateNormalPaintEntry();
|
||||||
|
if (ps == nullptr)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
ps->image_id = image_id;
|
ps->image_id = image_id;
|
||||||
ps->x = imagePos.x;
|
ps->x = imagePos.x;
|
||||||
ps->y = imagePos.y;
|
ps->y = imagePos.y;
|
||||||
|
@ -383,7 +385,7 @@ static paint_struct* PaintArrangeStructsHelperRotation(paint_struct* ps_next, ui
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<int TRotation> static void PaintSessionArrange(paint_session* session, bool)
|
template<int TRotation> static void PaintSessionArrange(PaintSessionCore* session, bool)
|
||||||
{
|
{
|
||||||
paint_struct* psHead = &session->PaintHead;
|
paint_struct* psHead = &session->PaintHead;
|
||||||
|
|
||||||
|
@ -423,7 +425,7 @@ template<int TRotation> static void PaintSessionArrange(paint_session* session,
|
||||||
*
|
*
|
||||||
* rct2: 0x00688217
|
* rct2: 0x00688217
|
||||||
*/
|
*/
|
||||||
void PaintSessionArrange(paint_session* session)
|
void PaintSessionArrange(PaintSessionCore* session)
|
||||||
{
|
{
|
||||||
switch (session->CurrentRotation)
|
switch (session->CurrentRotation)
|
||||||
{
|
{
|
||||||
|
@ -847,18 +849,18 @@ paint_struct* PaintAddImageAsChild(
|
||||||
*/
|
*/
|
||||||
bool PaintAttachToPreviousAttach(paint_session* session, uint32_t image_id, int16_t x, int16_t y)
|
bool PaintAttachToPreviousAttach(paint_session* session, uint32_t image_id, int16_t x, int16_t y)
|
||||||
{
|
{
|
||||||
if (session->NoPaintStructsAvailable())
|
auto* previousAttachedPS = session->LastAttachedPS;
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
attached_paint_struct* previousAttachedPS = session->LastAttachedPS;
|
|
||||||
if (previousAttachedPS == nullptr)
|
if (previousAttachedPS == nullptr)
|
||||||
{
|
{
|
||||||
return PaintAttachToPreviousPS(session, image_id, x, y);
|
return PaintAttachToPreviousPS(session, image_id, x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
attached_paint_struct* ps = session->AllocateAttachedPaintEntry();
|
auto* ps = session->AllocateAttachedPaintEntry();
|
||||||
|
if (ps == nullptr)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
ps->image_id = image_id;
|
ps->image_id = image_id;
|
||||||
ps->x = x;
|
ps->x = x;
|
||||||
ps->y = y;
|
ps->y = y;
|
||||||
|
@ -880,18 +882,18 @@ bool PaintAttachToPreviousAttach(paint_session* session, uint32_t image_id, int1
|
||||||
*/
|
*/
|
||||||
bool PaintAttachToPreviousPS(paint_session* session, uint32_t image_id, int16_t x, int16_t y)
|
bool PaintAttachToPreviousPS(paint_session* session, uint32_t image_id, int16_t x, int16_t y)
|
||||||
{
|
{
|
||||||
if (session->NoPaintStructsAvailable())
|
auto* masterPs = session->LastPS;
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
paint_struct* masterPs = session->LastPS;
|
|
||||||
if (masterPs == nullptr)
|
if (masterPs == nullptr)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
attached_paint_struct* ps = session->AllocateAttachedPaintEntry();
|
auto* ps = session->AllocateAttachedPaintEntry();
|
||||||
|
if (ps == nullptr)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
ps->image_id = image_id;
|
ps->image_id = image_id;
|
||||||
ps->x = x;
|
ps->x = x;
|
||||||
ps->y = y;
|
ps->y = y;
|
||||||
|
@ -918,7 +920,8 @@ void PaintFloatingMoneyEffect(
|
||||||
paint_session* session, money32 amount, rct_string_id string_id, int16_t y, int16_t z, int8_t y_offsets[], int16_t offset_x,
|
paint_session* session, money32 amount, rct_string_id string_id, int16_t y, int16_t z, int8_t y_offsets[], int16_t offset_x,
|
||||||
uint32_t rotation)
|
uint32_t rotation)
|
||||||
{
|
{
|
||||||
if (session->NoPaintStructsAvailable())
|
auto* ps = session->AllocateStringPaintEntry();
|
||||||
|
if (ps == nullptr)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -930,7 +933,6 @@ void PaintFloatingMoneyEffect(
|
||||||
};
|
};
|
||||||
const auto coord = translate_3d_to_2d_with_z(rotation, position);
|
const auto coord = translate_3d_to_2d_with_z(rotation, position);
|
||||||
|
|
||||||
paint_string_struct* ps = session->AllocateStringPaintEntry();
|
|
||||||
ps->string_id = string_id;
|
ps->string_id = string_id;
|
||||||
ps->next = nullptr;
|
ps->next = nullptr;
|
||||||
ps->args[0] = amount;
|
ps->args[0] = amount;
|
||||||
|
@ -966,3 +968,133 @@ void PaintDrawMoneyStructs(rct_drawpixelinfo* dpi, paint_string_struct* ps)
|
||||||
FontSpriteBase::MEDIUM);
|
FontSpriteBase::MEDIUM);
|
||||||
} while ((ps = ps->next) != nullptr);
|
} 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -15,6 +15,9 @@
|
||||||
#include "../interface/Colour.h"
|
#include "../interface/Colour.h"
|
||||||
#include "../world/Location.hpp"
|
#include "../world/Location.hpp"
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
struct TileElement;
|
struct TileElement;
|
||||||
enum class ViewportInteractionItem : uint8_t;
|
enum class ViewportInteractionItem : uint8_t;
|
||||||
|
|
||||||
|
@ -135,10 +138,58 @@ struct tunnel_entry
|
||||||
#define MAX_PAINT_QUADRANTS 512
|
#define MAX_PAINT_QUADRANTS 512
|
||||||
#define TUNNEL_MAX_COUNT 65
|
#define TUNNEL_MAX_COUNT 65
|
||||||
|
|
||||||
struct paint_session
|
/**
|
||||||
|
* 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
|
||||||
|
{
|
||||||
|
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();
|
||||||
|
size_t GetCount() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Node*> _available;
|
||||||
|
std::mutex _mutex;
|
||||||
|
|
||||||
|
Node* AllocateNode();
|
||||||
|
|
||||||
|
public:
|
||||||
|
~PaintEntryPool();
|
||||||
|
|
||||||
|
Chain Create();
|
||||||
|
void FreeNodes(Node* head);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PaintSessionCore
|
||||||
{
|
{
|
||||||
rct_drawpixelinfo DPI;
|
|
||||||
FixedVector<paint_entry, 4000> PaintStructs;
|
|
||||||
paint_struct* Quadrants[MAX_PAINT_QUADRANTS];
|
paint_struct* Quadrants[MAX_PAINT_QUADRANTS];
|
||||||
paint_struct* LastPS;
|
paint_struct* LastPS;
|
||||||
paint_string_struct* PSStringHead;
|
paint_string_struct* PSStringHead;
|
||||||
|
@ -168,38 +219,60 @@ struct paint_session
|
||||||
uint8_t Unk141E9DB;
|
uint8_t Unk141E9DB;
|
||||||
uint16_t WaterHeight;
|
uint16_t WaterHeight;
|
||||||
uint32_t TrackColours[4];
|
uint32_t TrackColours[4];
|
||||||
|
};
|
||||||
|
|
||||||
constexpr bool NoPaintStructsAvailable() noexcept
|
struct paint_session : public PaintSessionCore
|
||||||
{
|
{
|
||||||
return PaintStructs.size() >= PaintStructs.capacity();
|
rct_drawpixelinfo DPI;
|
||||||
}
|
PaintEntryPool::Chain PaintEntryChain;
|
||||||
|
|
||||||
constexpr paint_struct* AllocateNormalPaintEntry() noexcept
|
paint_struct* AllocateNormalPaintEntry() noexcept
|
||||||
{
|
{
|
||||||
LastPS = &PaintStructs.emplace_back().basic;
|
auto* entry = PaintEntryChain.Allocate();
|
||||||
return LastPS;
|
if (entry != nullptr)
|
||||||
}
|
|
||||||
|
|
||||||
constexpr attached_paint_struct* AllocateAttachedPaintEntry() noexcept
|
|
||||||
{
|
|
||||||
LastAttachedPS = &PaintStructs.emplace_back().attached;
|
|
||||||
return LastAttachedPS;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr paint_string_struct* AllocateStringPaintEntry() noexcept
|
|
||||||
{
|
|
||||||
auto* string = &PaintStructs.emplace_back().string;
|
|
||||||
if (LastPSString == nullptr)
|
|
||||||
{
|
{
|
||||||
PSStringHead = string;
|
LastPS = &entry->basic;
|
||||||
|
return LastPS;
|
||||||
}
|
}
|
||||||
else
|
return nullptr;
|
||||||
{
|
|
||||||
LastPSString->next = string;
|
|
||||||
}
|
|
||||||
LastPSString = string;
|
|
||||||
return LastPSString;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
attached_paint_struct* AllocateAttachedPaintEntry() noexcept
|
||||||
|
{
|
||||||
|
auto* entry = PaintEntryChain.Allocate();
|
||||||
|
if (entry != nullptr)
|
||||||
|
{
|
||||||
|
LastAttachedPS = &entry->attached;
|
||||||
|
return LastAttachedPS;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
paint_string_struct* AllocateStringPaintEntry() noexcept
|
||||||
|
{
|
||||||
|
auto* entry = PaintEntryChain.Allocate();
|
||||||
|
if (entry != nullptr)
|
||||||
|
{
|
||||||
|
auto* string = &entry->string;
|
||||||
|
if (LastPSString == nullptr)
|
||||||
|
{
|
||||||
|
PSStringHead = string;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LastPSString->next = string;
|
||||||
|
}
|
||||||
|
LastPSString = string;
|
||||||
|
return LastPSString;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RecordedPaintSession
|
||||||
|
{
|
||||||
|
PaintSessionCore Session;
|
||||||
|
std::vector<paint_entry> Entries;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern paint_session gPaintSession;
|
extern paint_session gPaintSession;
|
||||||
|
@ -260,7 +333,7 @@ void PaintFloatingMoneyEffect(
|
||||||
paint_session* PaintSessionAlloc(rct_drawpixelinfo* dpi, uint32_t viewFlags);
|
paint_session* PaintSessionAlloc(rct_drawpixelinfo* dpi, uint32_t viewFlags);
|
||||||
void PaintSessionFree(paint_session* session);
|
void PaintSessionFree(paint_session* session);
|
||||||
void PaintSessionGenerate(paint_session* session);
|
void PaintSessionGenerate(paint_session* session);
|
||||||
void PaintSessionArrange(paint_session* session);
|
void PaintSessionArrange(PaintSessionCore* session);
|
||||||
void PaintDrawStructs(paint_session* session);
|
void PaintDrawStructs(paint_session* session);
|
||||||
void PaintDrawMoneyStructs(rct_drawpixelinfo* dpi, paint_string_struct* ps);
|
void PaintDrawMoneyStructs(rct_drawpixelinfo* dpi, paint_string_struct* ps);
|
||||||
|
|
||||||
|
|
|
@ -151,7 +151,7 @@ paint_session* Painter::CreateSession(rct_drawpixelinfo* dpi, uint32_t viewFlags
|
||||||
session->ViewFlags = viewFlags;
|
session->ViewFlags = viewFlags;
|
||||||
session->QuadrantBackIndex = std::numeric_limits<uint32_t>::max();
|
session->QuadrantBackIndex = std::numeric_limits<uint32_t>::max();
|
||||||
session->QuadrantFrontIndex = 0;
|
session->QuadrantFrontIndex = 0;
|
||||||
session->PaintStructs.clear();
|
session->PaintEntryChain = _paintStructPool.Create();
|
||||||
|
|
||||||
std::fill(std::begin(session->Quadrants), std::end(session->Quadrants), nullptr);
|
std::fill(std::begin(session->Quadrants), std::end(session->Quadrants), nullptr);
|
||||||
session->LastPS = nullptr;
|
session->LastPS = nullptr;
|
||||||
|
@ -167,5 +167,6 @@ paint_session* Painter::CreateSession(rct_drawpixelinfo* dpi, uint32_t viewFlags
|
||||||
|
|
||||||
void Painter::ReleaseSession(paint_session* session)
|
void Painter::ReleaseSession(paint_session* session)
|
||||||
{
|
{
|
||||||
|
session->PaintEntryChain.Clear();
|
||||||
_freePaintSessions.push_back(session);
|
_freePaintSessions.push_back(session);
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@ namespace OpenRCT2
|
||||||
std::shared_ptr<Ui::IUiContext> const _uiContext;
|
std::shared_ptr<Ui::IUiContext> const _uiContext;
|
||||||
std::vector<std::unique_ptr<paint_session>> _paintSessionPool;
|
std::vector<std::unique_ptr<paint_session>> _paintSessionPool;
|
||||||
std::vector<paint_session*> _freePaintSessions;
|
std::vector<paint_session*> _freePaintSessions;
|
||||||
|
PaintEntryPool _paintStructPool;
|
||||||
time_t _lastSecond = 0;
|
time_t _lastSecond = 0;
|
||||||
int32_t _currentFPS = 0;
|
int32_t _currentFPS = 0;
|
||||||
int32_t _frames = 0;
|
int32_t _frames = 0;
|
||||||
|
|
Loading…
Reference in New Issue