From 37c380974424fc2c4fedab4e59e1d2aef124f1bd Mon Sep 17 00:00:00 2001 From: Matt Date: Tue, 18 Dec 2018 16:40:28 +0100 Subject: [PATCH] Implement partial multicore rendering --- data/language/en-GB.txt | 2 + .../drawing/engines/opengl/TextureCache.cpp | 51 +++++--- .../drawing/engines/opengl/TextureCache.h | 2 + src/openrct2-ui/windows/Options.cpp | 11 +- src/openrct2/config/Config.cpp | 2 + src/openrct2/config/Config.h | 1 + src/openrct2/core/JobPool.hpp | 6 +- src/openrct2/drawing/ScrollingText.cpp | 2 +- src/openrct2/interface/Viewport.cpp | 123 ++++++++++-------- src/openrct2/localisation/Localisation.cpp | 6 +- src/openrct2/localisation/Localisation.h | 6 +- src/openrct2/localisation/StringIds.h | 2 + src/openrct2/paint/Paint.cpp | 37 +++--- src/openrct2/paint/Paint.h | 8 +- src/openrct2/paint/sprite/Paint.Litter.cpp | 2 +- src/openrct2/paint/sprite/Paint.Misc.cpp | 2 +- src/openrct2/paint/sprite/Paint.Peep.cpp | 2 +- src/openrct2/paint/sprite/Paint.Sprite.cpp | 4 +- .../paint/tile_element/Paint.Banner.cpp | 2 +- .../paint/tile_element/Paint.Entrance.cpp | 2 +- .../paint/tile_element/Paint.LargeScenery.cpp | 4 +- .../paint/tile_element/Paint.Path.cpp | 2 +- .../paint/tile_element/Paint.SmallScenery.cpp | 2 +- .../paint/tile_element/Paint.Surface.cpp | 2 +- .../paint/tile_element/Paint.TileElement.cpp | 4 +- src/openrct2/ride/TrackPaint.cpp | 2 +- src/openrct2/ride/VehiclePaint.cpp | 2 +- src/openrct2/ride/coaster/VirginiaReel.cpp | 2 +- src/openrct2/ride/gentle/HauntedHouse.cpp | 2 +- src/openrct2/ride/gentle/MerryGoRound.cpp | 2 +- src/openrct2/ride/gentle/MiniGolf.cpp | 4 +- src/openrct2/ride/gentle/SpiralSlide.cpp | 2 +- src/openrct2/ride/thrill/Enterprise.cpp | 2 +- src/openrct2/ride/thrill/LaunchedFreefall.cpp | 2 +- src/openrct2/ride/thrill/MagicCarpet.cpp | 2 +- src/openrct2/ride/thrill/PirateShip.cpp | 2 +- src/openrct2/ride/thrill/TopSpin.cpp | 2 +- src/openrct2/ride/thrill/Twist.cpp | 2 +- src/openrct2/ride/water/RiverRapids.cpp | 2 +- test/testpaint/TestPaint.cpp | 2 +- 40 files changed, 196 insertions(+), 123 deletions(-) diff --git a/data/language/en-GB.txt b/data/language/en-GB.txt index 0630b1c4cd..865637d53c 100644 --- a/data/language/en-GB.txt +++ b/data/language/en-GB.txt @@ -3751,6 +3751,8 @@ STR_6301 :{SMALLFONT}{BLACK}Copy the selected object name to the clipboard. STR_6302 :{SMALLFONT}{BLACK}Copy the entire list of missing objects to the clipboard. STR_6303 :Downloading object ({COMMA16} / {COMMA16}): [{STRING}] STR_6304 :Open scenery picker +STR_6305 :Multithreading +STR_6306 :{SMALLFONT}{BLACK}Use multiple threads to render ############# # Scenarios # diff --git a/src/openrct2-ui/drawing/engines/opengl/TextureCache.cpp b/src/openrct2-ui/drawing/engines/opengl/TextureCache.cpp index caf65149a0..117b188b0b 100644 --- a/src/openrct2-ui/drawing/engines/opengl/TextureCache.cpp +++ b/src/openrct2-ui/drawing/engines/opengl/TextureCache.cpp @@ -30,6 +30,8 @@ TextureCache::~TextureCache() void TextureCache::InvalidateImage(uint32_t image) { + std::unique_lock lock(_mutex); + uint32_t index = _indexMap[image]; if (index == UNUSED_INDEX) return; @@ -61,18 +63,28 @@ void TextureCache::InvalidateImage(uint32_t image) BasicTextureInfo TextureCache::GetOrLoadImageTexture(uint32_t image) { + uint32_t index; + image &= 0x7FFFF; - uint32_t index = _indexMap[image]; - if (index != UNUSED_INDEX) + // Try to read cached texture first. { - const auto& info = _textureCache[index]; - return { - info.index, - info.normalizedBounds, - }; + std::shared_lock lock(_mutex); + + index = _indexMap[image]; + if (index != UNUSED_INDEX) + { + const auto& info = _textureCache[index]; + return { + info.index, + info.normalizedBounds, + }; + } } + // Load new texture. + std::unique_lock lock(_mutex); + index = (uint32_t)_textureCache.size(); AtlasTextureInfo info = LoadImageTexture(image); @@ -87,18 +99,27 @@ BasicTextureInfo TextureCache::GetOrLoadGlyphTexture(uint32_t image, uint8_t* pa { GlyphId glyphId; glyphId.Image = image; - std::copy_n(palette, sizeof(glyphId.Palette), (uint8_t*)&glyphId.Palette); - auto kvp = _glyphTextureMap.find(glyphId); - if (kvp != _glyphTextureMap.end()) + // Try to read cached texture first. { - const auto& info = kvp->second; - return { - info.index, - info.normalizedBounds, - }; + std::shared_lock lock(_mutex); + + std::copy_n(palette, sizeof(glyphId.Palette), (uint8_t*)&glyphId.Palette); + + auto kvp = _glyphTextureMap.find(glyphId); + if (kvp != _glyphTextureMap.end()) + { + const auto& info = kvp->second; + return { + info.index, + info.normalizedBounds, + }; + } } + // Load new texture. + std::unique_lock lock(_mutex); + auto cacheInfo = LoadGlyphTexture(image, palette); auto it = _glyphTextureMap.insert(std::make_pair(glyphId, cacheInfo)); diff --git a/src/openrct2-ui/drawing/engines/opengl/TextureCache.h b/src/openrct2-ui/drawing/engines/opengl/TextureCache.h index c763e5c448..bd3f8c6b42 100644 --- a/src/openrct2-ui/drawing/engines/opengl/TextureCache.h +++ b/src/openrct2-ui/drawing/engines/opengl/TextureCache.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -198,6 +199,7 @@ private: std::array _indexMap; GLuint _paletteTexture = 0; + std::shared_mutex _mutex; public: TextureCache(); diff --git a/src/openrct2-ui/windows/Options.cpp b/src/openrct2-ui/windows/Options.cpp index d870dfa642..c41600f2fd 100644 --- a/src/openrct2-ui/windows/Options.cpp +++ b/src/openrct2-ui/windows/Options.cpp @@ -87,6 +87,7 @@ enum WINDOW_OPTIONS_WIDGET_IDX { WIDX_STEAM_OVERLAY_PAUSE, WIDX_UNCAP_FPS_CHECKBOX, WIDX_SHOW_FPS_CHECKBOX, + WIDX_MULTITHREADING_CHECKBOX, WIDX_USE_VSYNC_CHECKBOX, WIDX_MINIMIZE_FOCUS_LOSS, @@ -236,7 +237,8 @@ static rct_widget window_options_display_widgets[] = { { WWT_CHECKBOX, 1, 25, 290, 144, 155, STR_STEAM_OVERLAY_PAUSE, STR_STEAM_OVERLAY_PAUSE_TIP }, // Pause on steam overlay { WWT_CHECKBOX, 1, 11, 153, 161, 172, STR_UNCAP_FPS, STR_UNCAP_FPS_TIP }, // Uncap fps { WWT_CHECKBOX, 1, 155, 290, 161, 172, STR_SHOW_FPS, STR_SHOW_FPS_TIP }, // Show fps - { WWT_CHECKBOX, 1, 11, 290, 176, 187, STR_USE_VSYNC, STR_USE_VSYNC_TIP }, // Use vsync + { WWT_CHECKBOX, 1, 155, 290, 176, 187, STR_MULTITHREADING, STR_MULTITHREADING_TIP }, // Multithreading + { WWT_CHECKBOX, 1, 11, 153, 176, 187, STR_USE_VSYNC, STR_USE_VSYNC_TIP }, // Use vsync { WWT_CHECKBOX, 1, 11, 290, 191, 202, STR_MINIMISE_FULLSCREEN_ON_FOCUS_LOSS, STR_MINIMISE_FULLSCREEN_ON_FOCUS_LOSS_TIP }, // Minimise fullscreen focus loss { WIDGETS_END }, }; @@ -522,6 +524,7 @@ static uint64_t window_options_page_enabled_widgets[] = { (1 << WIDX_UNCAP_FPS_CHECKBOX) | (1 << WIDX_USE_VSYNC_CHECKBOX) | (1 << WIDX_SHOW_FPS_CHECKBOX) | + (1 << WIDX_MULTITHREADING_CHECKBOX) | (1 << WIDX_MINIMIZE_FOCUS_LOSS) | (1 << WIDX_STEAM_OVERLAY_PAUSE) | (1 << WIDX_SCALE) | @@ -693,6 +696,11 @@ static void window_options_mouseup(rct_window* w, rct_widgetindex widgetIndex) config_save_default(); window_invalidate(w); break; + case WIDX_MULTITHREADING_CHECKBOX: + gConfigGeneral.multithreading ^= 1; + config_save_default(); + window_invalidate(w); + break; case WIDX_MINIMIZE_FOCUS_LOSS: gConfigGeneral.minimize_fullscreen_focus_loss ^= 1; platform_refresh_video(false); @@ -1711,6 +1719,7 @@ static void window_options_invalidate(rct_window* w) widget_set_checkbox_value(w, WIDX_UNCAP_FPS_CHECKBOX, gConfigGeneral.uncap_fps); widget_set_checkbox_value(w, WIDX_USE_VSYNC_CHECKBOX, gConfigGeneral.use_vsync); widget_set_checkbox_value(w, WIDX_SHOW_FPS_CHECKBOX, gConfigGeneral.show_fps); + widget_set_checkbox_value(w, WIDX_MULTITHREADING_CHECKBOX, gConfigGeneral.multithreading); widget_set_checkbox_value(w, WIDX_MINIMIZE_FOCUS_LOSS, gConfigGeneral.minimize_fullscreen_focus_loss); widget_set_checkbox_value(w, WIDX_STEAM_OVERLAY_PAUSE, gConfigGeneral.steam_overlay_pause); diff --git a/src/openrct2/config/Config.cpp b/src/openrct2/config/Config.cpp index 15e31378f8..3eb02ff8b7 100644 --- a/src/openrct2/config/Config.cpp +++ b/src/openrct2/config/Config.cpp @@ -195,6 +195,7 @@ namespace Config model->window_scale = reader->GetFloat("window_scale", platform_get_default_scale()); model->scale_quality = reader->GetEnum("scale_quality", SCALE_QUALITY_SMOOTH_NN, Enum_ScaleQuality); model->show_fps = reader->GetBoolean("show_fps", false); + model->multithreading = reader->GetBoolean("multithreading", true); model->trap_cursor = reader->GetBoolean("trap_cursor", false); model->auto_open_shops = reader->GetBoolean("auto_open_shops", false); model->scenario_select_mode = reader->GetInt32("scenario_select_mode", SCENARIO_SELECT_MODE_ORIGIN); @@ -268,6 +269,7 @@ namespace Config writer->WriteFloat("window_scale", model->window_scale); writer->WriteEnum("scale_quality", model->scale_quality, Enum_ScaleQuality); writer->WriteBoolean("show_fps", model->show_fps); + writer->WriteBoolean("multithreading", model->multithreading); writer->WriteBoolean("trap_cursor", model->trap_cursor); writer->WriteBoolean("auto_open_shops", model->auto_open_shops); writer->WriteInt32("scenario_select_mode", model->scenario_select_mode); diff --git a/src/openrct2/config/Config.h b/src/openrct2/config/Config.h index e885512bf2..5d6ffc477e 100644 --- a/src/openrct2/config/Config.h +++ b/src/openrct2/config/Config.h @@ -32,6 +32,7 @@ struct GeneralConfiguration bool uncap_fps; bool use_vsync; bool show_fps; + bool multithreading; bool minimize_fullscreen_focus_loss; // Map rendering diff --git a/src/openrct2/core/JobPool.hpp b/src/openrct2/core/JobPool.hpp index 8dd6ec10af..f4a6f334ec 100644 --- a/src/openrct2/core/JobPool.hpp +++ b/src/openrct2/core/JobPool.hpp @@ -9,6 +9,7 @@ #pragma once +#include #include #include #include @@ -45,9 +46,10 @@ private: typedef std::unique_lock unique_lock; public: - JobPool() + JobPool(size_t maxThreads = 255) { - for (size_t n = 0; n < std::thread::hardware_concurrency(); n++) + maxThreads = std::min(maxThreads, std::thread::hardware_concurrency()); + for (size_t n = 0; n < maxThreads; n++) { _threads.emplace_back(&JobPool::ProcessQueue, this); } diff --git a/src/openrct2/drawing/ScrollingText.cpp b/src/openrct2/drawing/ScrollingText.cpp index e31abdc74b..0d35edea29 100644 --- a/src/openrct2/drawing/ScrollingText.cpp +++ b/src/openrct2/drawing/ScrollingText.cpp @@ -1474,7 +1474,7 @@ int32_t scrolling_text_setup(paint_session* session, rct_string_id stringId, uin { assert(scrollingMode < MAX_SCROLLING_TEXT_MODES); - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if (dpi->zoom_level != 0) return SPR_SCROLLING_TEXT_DEFAULT; diff --git a/src/openrct2/interface/Viewport.cpp b/src/openrct2/interface/Viewport.cpp index 855f98a3d0..5ecf915dba 100644 --- a/src/openrct2/interface/Viewport.cpp +++ b/src/openrct2/interface/Viewport.cpp @@ -14,6 +14,7 @@ #include "../Input.h" #include "../OpenRCT2.h" #include "../config/Config.h" +#include "../core/JobPool.hpp" #include "../drawing/Drawing.h" #include "../paint/Paint.h" #include "../peep/Staff.h" @@ -42,6 +43,7 @@ rct_viewport g_viewport_list[MAX_VIEWPORT_COUNT]; rct_viewport* g_music_tracking_viewport; static TileElement* _interaction_element = nullptr; +static std::unique_ptr _paintJobs; int16_t gSavedViewX; int16_t gSavedViewY; @@ -60,7 +62,6 @@ static int16_t _interactionMapX; static int16_t _interactionMapY; static uint16_t _unk9AC154; -static void viewport_paint_column(rct_drawpixelinfo* dpi, uint32_t viewFlags, std::vector* sessions); static void viewport_paint_weather_gloom(rct_drawpixelinfo* dpi); /** @@ -833,6 +834,41 @@ void viewport_render( #endif } +static void viewport_fill_column(paint_session* session) +{ + paint_session_generate(session); + paint_session_arrange(session); +} + +static void viewport_paint_column(paint_session* session) +{ + if (session->ViewFlags + & (VIEWPORT_FLAG_HIDE_VERTICAL | VIEWPORT_FLAG_HIDE_BASE | VIEWPORT_FLAG_UNDERGROUND_INSIDE | VIEWPORT_FLAG_CLIP_VIEW)) + { + uint8_t colour = 10; + if (session->ViewFlags & VIEWPORT_FLAG_INVISIBLE_SPRITES) + { + colour = 0; + } + gfx_clear(&session->DPI, colour); + } + + paint_draw_structs(session); + + if (gConfigGeneral.render_weather_gloom && !gTrackDesignSaveMode && !(session->ViewFlags & VIEWPORT_FLAG_INVISIBLE_SPRITES) + && !(session->ViewFlags & VIEWPORT_FLAG_HIGHLIGHT_PATH_ISSUES)) + { + viewport_paint_weather_gloom(&session->DPI); + } + + if (session->PSStringHead != nullptr) + { + paint_draw_money_structs(&session->DPI, session->PSStringHead); + } + + paint_session_free(session); +} + /** * * rct2: 0x00685CBF @@ -880,82 +916,65 @@ void viewport_paint( // this as well as the [x += 32] in the loop causes signed integer overflow -> undefined behaviour. int16_t rightBorder = dpi1.x + dpi1.width; - // Splits the area into 32 pixel columns and renders them - int16_t start_x = floor2(dpi1.x, 32); - if (sessions != nullptr) + std::vector columns; + + bool useMultithreading = gConfigGeneral.multithreading; + if (window_get_main() != nullptr && viewport != window_get_main()->viewport) + useMultithreading = false; + + if (useMultithreading == true && _paintJobs == nullptr) { - sessions->reserve((rightBorder - start_x) / 32); + _paintJobs = std::make_unique(); } - for (int16_t columnx = start_x; columnx < rightBorder; columnx += 32) + else if (useMultithreading == false && _paintJobs != nullptr) { - rct_drawpixelinfo dpi2 = dpi1; - if (columnx >= dpi2.x) + _paintJobs.reset(); + } + + // Splits the area into 32 pixel columns and renders them + size_t index = 0; + for (x = floor2(dpi1.x, 32); x < rightBorder; x += 32, index++) + { + paint_session* session = paint_session_alloc(&dpi1, viewFlags); + columns.push_back(session); + + rct_drawpixelinfo& dpi2 = session->DPI; + if (x >= dpi2.x) { - int16_t leftPitch = columnx - dpi2.x; + int16_t leftPitch = x - dpi2.x; dpi2.width -= leftPitch; dpi2.bits += leftPitch >> dpi2.zoom_level; dpi2.pitch += leftPitch >> dpi2.zoom_level; - dpi2.x = columnx; + dpi2.x = x; } int16_t paintRight = dpi2.x + dpi2.width; - if (paintRight >= columnx + 32) + if (paintRight >= x + 32) { - int16_t rightPitch = paintRight - columnx - 32; + int16_t rightPitch = paintRight - x - 32; paintRight -= rightPitch; dpi2.pitch += rightPitch >> dpi2.zoom_level; } dpi2.width = paintRight - dpi2.x; - viewport_paint_column(&dpi2, viewFlags, sessions); - } -} - -static void viewport_paint_column(rct_drawpixelinfo* dpi, uint32_t viewFlags, std::vector* sessions) -{ - if (viewFlags - & (VIEWPORT_FLAG_HIDE_VERTICAL | VIEWPORT_FLAG_HIDE_BASE | VIEWPORT_FLAG_UNDERGROUND_INSIDE | VIEWPORT_FLAG_CLIP_VIEW)) - { - uint8_t colour = 10; - if (viewFlags & VIEWPORT_FLAG_INVISIBLE_SPRITES) + if (useMultithreading) { - colour = 0; + _paintJobs->AddTask([session]() -> void { viewport_fill_column(session); }); } - gfx_clear(dpi, colour); - } - - paint_session* session = paint_session_alloc(dpi, viewFlags); - paint_session_generate(session); - // Perform a deep copy of the paint session, use relative offsets. - // This is done to extract the session for benchmark. - if (sessions != nullptr) - { - sessions->push_back(*session); - paint_session* session_copy = &sessions->at(sessions->size() - 1); - - // Mind the offset needs to be calculated against the original `session`, not `session_copy` - for (auto& ps : session_copy->PaintStructs) + else { - ps.basic.next_quadrant_ps = (paint_struct*)(ps.basic.next_quadrant_ps ? int(ps.basic.next_quadrant_ps - &session->PaintStructs[0].basic) : std::size(session->PaintStructs)); - } - for (auto& quad : session_copy->Quadrants) - { - quad = (paint_struct*)(quad ? int(quad - &session->PaintStructs[0].basic) : std::size(session->Quadrants)); + viewport_fill_column(session); } } - paint_session_arrange(session); - paint_draw_structs(session); - paint_session_free(session); - if (gConfigGeneral.render_weather_gloom && !gTrackDesignSaveMode && !(viewFlags & VIEWPORT_FLAG_INVISIBLE_SPRITES) - && !(viewFlags & VIEWPORT_FLAG_HIGHLIGHT_PATH_ISSUES)) + if (useMultithreading) { - viewport_paint_weather_gloom(dpi); + _paintJobs->Join(); } - if (session->PSStringHead != nullptr) + for (auto&& column : columns) { - paint_draw_money_structs(dpi, session->PSStringHead); + viewport_paint_column(column); } } @@ -1590,7 +1609,7 @@ static bool sub_679023(rct_drawpixelinfo* dpi, int32_t imageId, int32_t x, int32 static void sub_68862C(paint_session* session) { paint_struct* ps = &session->PaintHead; - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; while ((ps = ps->next_quadrant_ps) != nullptr) { diff --git a/src/openrct2/localisation/Localisation.cpp b/src/openrct2/localisation/Localisation.cpp index 99ae0930be..d62b5de7d8 100644 --- a/src/openrct2/localisation/Localisation.cpp +++ b/src/openrct2/localisation/Localisation.cpp @@ -35,9 +35,9 @@ #include #include -char gCommonStringFormatBuffer[512]; -uint8_t gCommonFormatArgs[80]; -uint8_t gMapTooltipFormatArgs[40]; +thread_local char gCommonStringFormatBuffer[512]; +thread_local uint8_t gCommonFormatArgs[80]; +thread_local uint8_t gMapTooltipFormatArgs[40]; #ifdef DEBUG // Set to true before a string format call to see details of the formatting. diff --git a/src/openrct2/localisation/Localisation.h b/src/openrct2/localisation/Localisation.h index 6039265b77..ef848aec28 100644 --- a/src/openrct2/localisation/Localisation.h +++ b/src/openrct2/localisation/Localisation.h @@ -74,9 +74,9 @@ extern const char real_name_initials[16]; extern const char* real_names[1024]; extern utf8 gUserStrings[MAX_USER_STRINGS][USER_STRING_MAX_LENGTH]; -extern char gCommonStringFormatBuffer[512]; -extern uint8_t gCommonFormatArgs[80]; -extern uint8_t gMapTooltipFormatArgs[40]; +extern thread_local char gCommonStringFormatBuffer[512]; +extern thread_local uint8_t gCommonFormatArgs[80]; +extern thread_local uint8_t gMapTooltipFormatArgs[40]; extern bool gDebugStringFormatting; extern const rct_string_id SpeedNames[5]; diff --git a/src/openrct2/localisation/StringIds.h b/src/openrct2/localisation/StringIds.h index 9395aa69ee..6a03538327 100644 --- a/src/openrct2/localisation/StringIds.h +++ b/src/openrct2/localisation/StringIds.h @@ -3934,6 +3934,8 @@ enum STR_DOWNLOADING_OBJECTS = 6303, STR_SHORTCUT_OPEN_SCENERY_PICKER = 6304, + STR_MULTITHREADING = 6305, + STR_MULTITHREADING_TIP = 6306, // Have to include resource strings (from scenarios and objects) for the time being now that language is partially working STR_COUNT = 32768 diff --git a/src/openrct2/paint/Paint.cpp b/src/openrct2/paint/Paint.cpp index d66483f260..853c5090d4 100644 --- a/src/openrct2/paint/Paint.cpp +++ b/src/openrct2/paint/Paint.cpp @@ -18,14 +18,15 @@ #include "tile_element/Paint.TileElement.h" #include +#include +#include // Globals for paint clipping uint8_t gClipHeight = 128; // Default to middle value LocationXY8 gClipSelectionA = { 0, 0 }; LocationXY8 gClipSelectionB = { MAXIMUM_MAP_SIZE_TECHNICAL - 1, MAXIMUM_MAP_SIZE_TECHNICAL - 1 }; -paint_session gPaintSession; -static bool _paintSessionInUse; +static std::vector _freePaintSessions; static constexpr const uint8_t BoundBoxDebugColours[] = { 0, // NONE @@ -56,7 +57,7 @@ static uint32_t paint_ps_colourify_image(uint32_t imageId, uint8_t spriteType, u static void paint_session_init(paint_session* session, rct_drawpixelinfo* dpi, uint32_t viewFlags) { - session->DPI = dpi; + session->DPI = *dpi; session->EndOfPaintStructArray = &session->PaintStructs[4000 - 1]; session->NextFreePaintStruct = session->PaintStructs; session->LastRootPS = nullptr; @@ -132,7 +133,7 @@ static paint_struct* sub_9819_c( int32_t right = left + g1->width; int32_t top = bottom + g1->height; - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if (right <= dpi->x) return nullptr; @@ -192,7 +193,7 @@ static paint_struct* sub_9819_c( */ void paint_session_generate(paint_session* session) { - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; LocationXY16 mapTile = { (int16_t)(dpi->x & 0xFFE0), (int16_t)((dpi->y - 16) & 0xFFE0) }; int16_t half_x = mapTile.x >> 1; @@ -458,7 +459,6 @@ void paint_session_arrange(paint_session* session) ps->next_quadrant_ps = nullptr; uint32_t quadrantIndex = session->QuadrantBackIndex; - const uint8_t rotation = get_current_rotation(); if (quadrantIndex != UINT32_MAX) { do @@ -477,19 +477,19 @@ void paint_session_arrange(paint_session* session) } while (++quadrantIndex <= session->QuadrantFrontIndex); paint_struct* ps_cache = paint_arrange_structs_helper( - psHead, session->QuadrantBackIndex & 0xFFFF, PAINT_QUADRANT_FLAG_NEXT, rotation); + psHead, session->QuadrantBackIndex & 0xFFFF, PAINT_QUADRANT_FLAG_NEXT, session->CurrentRotation); quadrantIndex = session->QuadrantBackIndex; while (++quadrantIndex < session->QuadrantFrontIndex) { - ps_cache = paint_arrange_structs_helper(ps_cache, quadrantIndex & 0xFFFF, 0, rotation); + ps_cache = paint_arrange_structs_helper(ps_cache, quadrantIndex & 0xFFFF, 0, session->CurrentRotation); } } } static void paint_draw_struct(paint_session* session, paint_struct* ps) { - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; int16_t x = ps->x; int16_t y = ps->y; @@ -732,10 +732,17 @@ static void draw_pixel_info_crop_by_zoom(rct_drawpixelinfo* dpi) paint_session* paint_session_alloc(rct_drawpixelinfo* dpi, uint32_t viewFlags) { - // Currently limited to just one session at a time - assert(!_paintSessionInUse); - _paintSessionInUse = true; - paint_session* session = &gPaintSession; + paint_session* session = nullptr; + + if (_freePaintSessions.empty()) + { + session = new paint_session(); + } + else + { + session = _freePaintSessions[_freePaintSessions.size() - 1]; + _freePaintSessions.resize(_freePaintSessions.size() - 1); + } paint_session_init(session, dpi, viewFlags); return session; @@ -743,7 +750,7 @@ paint_session* paint_session_alloc(rct_drawpixelinfo* dpi, uint32_t viewFlags) void paint_session_free([[maybe_unused]] paint_session* session) { - _paintSessionInUse = false; + _freePaintSessions.push_back(session); } /** @@ -845,7 +852,7 @@ paint_struct* sub_98196C( int16_t right = left + g1Element->width; int16_t top = bottom + g1Element->height; - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if (right <= dpi->x) return nullptr; diff --git a/src/openrct2/paint/Paint.h b/src/openrct2/paint/Paint.h index 85796488bf..039245c955 100644 --- a/src/openrct2/paint/Paint.h +++ b/src/openrct2/paint/Paint.h @@ -137,9 +137,15 @@ struct tunnel_entry #define MAX_PAINT_QUADRANTS 512 #define TUNNEL_MAX_COUNT 65 +struct paint_pending +{ + rct_drawpixelinfo dpi; + uint32_t viewFlags; +}; + struct paint_session { - rct_drawpixelinfo* DPI; + rct_drawpixelinfo DPI; paint_entry PaintStructs[4000]; paint_struct* Quadrants[MAX_PAINT_QUADRANTS]; paint_struct PaintHead; diff --git a/src/openrct2/paint/sprite/Paint.Litter.cpp b/src/openrct2/paint/sprite/Paint.Litter.cpp index 0c37d0840b..ece57eb724 100644 --- a/src/openrct2/paint/sprite/Paint.Litter.cpp +++ b/src/openrct2/paint/sprite/Paint.Litter.cpp @@ -69,7 +69,7 @@ void litter_paint(paint_session* session, const rct_litter* litter, int32_t imag { rct_drawpixelinfo* dpi; - dpi = session->DPI; + dpi = &session->DPI; if (dpi->zoom_level != 0) return; // If zoomed at all no litter drawn diff --git a/src/openrct2/paint/sprite/Paint.Misc.cpp b/src/openrct2/paint/sprite/Paint.Misc.cpp index b4e668e11a..2e598049f6 100644 --- a/src/openrct2/paint/sprite/Paint.Misc.cpp +++ b/src/openrct2/paint/sprite/Paint.Misc.cpp @@ -30,7 +30,7 @@ const uint32_t vehicle_particle_base_sprites[] = { */ void misc_paint(paint_session* session, const rct_sprite* misc, int32_t imageDirection) { - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; switch (misc->steam_particle.type) { diff --git a/src/openrct2/paint/sprite/Paint.Peep.cpp b/src/openrct2/paint/sprite/Paint.Peep.cpp index ea7586fb40..69d33e5d4a 100644 --- a/src/openrct2/paint/sprite/Paint.Peep.cpp +++ b/src/openrct2/paint/sprite/Paint.Peep.cpp @@ -56,7 +56,7 @@ void peep_paint(paint_session* session, const Peep* peep, int32_t imageDirection } #endif - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if (dpi->zoom_level > 2) { return; diff --git a/src/openrct2/paint/sprite/Paint.Sprite.cpp b/src/openrct2/paint/sprite/Paint.Sprite.cpp index 6944c91d65..6e5f3a9185 100644 --- a/src/openrct2/paint/sprite/Paint.Sprite.cpp +++ b/src/openrct2/paint/sprite/Paint.Sprite.cpp @@ -40,7 +40,7 @@ void sprite_paint_setup(paint_session* session, const uint16_t x, const uint16_t return; } - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if (dpi->zoom_level > 2) { return; @@ -89,7 +89,7 @@ void sprite_paint_setup(paint_session* session, const uint16_t x, const uint16_t } } - dpi = session->DPI; + dpi = &session->DPI; if (dpi->y + dpi->height <= spr->generic.sprite_top || spr->generic.sprite_bottom <= dpi->y || dpi->x + dpi->width <= spr->generic.sprite_left || spr->generic.sprite_right <= dpi->x) diff --git a/src/openrct2/paint/tile_element/Paint.Banner.cpp b/src/openrct2/paint/tile_element/Paint.Banner.cpp index 35378767e3..2471f8c287 100644 --- a/src/openrct2/paint/tile_element/Paint.Banner.cpp +++ b/src/openrct2/paint/tile_element/Paint.Banner.cpp @@ -34,7 +34,7 @@ const LocationXY16 BannerBoundBoxes[][2] = { void banner_paint(paint_session* session, uint8_t direction, int32_t height, const TileElement* tile_element) { uint16_t boundBoxOffsetX, boundBoxOffsetY, boundBoxOffsetZ; - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; session->InteractionType = VIEWPORT_INTERACTION_ITEM_BANNER; diff --git a/src/openrct2/paint/tile_element/Paint.Entrance.cpp b/src/openrct2/paint/tile_element/Paint.Entrance.cpp index 406db6e812..edcb400de9 100644 --- a/src/openrct2/paint/tile_element/Paint.Entrance.cpp +++ b/src/openrct2/paint/tile_element/Paint.Entrance.cpp @@ -334,7 +334,7 @@ void entrance_paint(paint_session* session, uint8_t direction, int32_t height, c { session->InteractionType = VIEWPORT_INTERACTION_ITEM_LABEL; - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if (session->ViewFlags & VIEWPORT_FLAG_PATH_HEIGHTS && dpi->zoom_level == 0) { diff --git a/src/openrct2/paint/tile_element/Paint.LargeScenery.cpp b/src/openrct2/paint/tile_element/Paint.LargeScenery.cpp index b5f9677c74..ea8fd3f599 100644 --- a/src/openrct2/paint/tile_element/Paint.LargeScenery.cpp +++ b/src/openrct2/paint/tile_element/Paint.LargeScenery.cpp @@ -293,7 +293,7 @@ void large_scenery_paint(paint_session* session, uint8_t direction, uint16_t hei return; } } - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if (dpi->zoom_level > 1) { large_scenery_paint_supports(session, direction, height, tileElement, dword_F4387C, tile); @@ -400,7 +400,7 @@ void large_scenery_paint(paint_session* session, uint8_t direction, uint16_t hei } return; } - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if (dpi->zoom_level > 0) { large_scenery_paint_supports(session, direction, height, tileElement, dword_F4387C, tile); diff --git a/src/openrct2/paint/tile_element/Paint.Path.cpp b/src/openrct2/paint/tile_element/Paint.Path.cpp index 061d3d84dc..26d0b8d3aa 100644 --- a/src/openrct2/paint/tile_element/Paint.Path.cpp +++ b/src/openrct2/paint/tile_element/Paint.Path.cpp @@ -682,7 +682,7 @@ static void sub_6A3F61( // Probably drawing benches etc. - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if (dpi->zoom_level <= 1) { diff --git a/src/openrct2/paint/tile_element/Paint.SmallScenery.cpp b/src/openrct2/paint/tile_element/Paint.SmallScenery.cpp index c2728279b4..5c204c352f 100644 --- a/src/openrct2/paint/tile_element/Paint.SmallScenery.cpp +++ b/src/openrct2/paint/tile_element/Paint.SmallScenery.cpp @@ -176,7 +176,7 @@ void scenery_paint(paint_session* session, uint8_t direction, int32_t height, co if (scenery_small_entry_has_flag(entry, SMALL_SCENERY_FLAG_ANIMATED)) { - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if ((scenery_small_entry_has_flag(entry, SMALL_SCENERY_FLAG_VISIBLE_WHEN_ZOOMED)) || (dpi->zoom_level <= 1)) { // 6E01A9: diff --git a/src/openrct2/paint/tile_element/Paint.Surface.cpp b/src/openrct2/paint/tile_element/Paint.Surface.cpp index 0ec039b1a3..8833f7b3da 100644 --- a/src/openrct2/paint/tile_element/Paint.Surface.cpp +++ b/src/openrct2/paint/tile_element/Paint.Surface.cpp @@ -907,7 +907,7 @@ static void viewport_surface_draw_water_side_top( */ void surface_paint(paint_session* session, uint8_t direction, uint16_t height, const TileElement* tileElement) { - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; session->InteractionType = VIEWPORT_INTERACTION_ITEM_TERRAIN; session->DidPassSurface = true; session->SurfaceElement = tileElement; diff --git a/src/openrct2/paint/tile_element/Paint.TileElement.cpp b/src/openrct2/paint/tile_element/Paint.TileElement.cpp index ac3098f802..0632f8f7c0 100644 --- a/src/openrct2/paint/tile_element/Paint.TileElement.cpp +++ b/src/openrct2/paint/tile_element/Paint.TileElement.cpp @@ -114,7 +114,7 @@ static void blank_tiles_paint(paint_session* session, int32_t x, int32_t y) dx -= 16; int32_t bx = dx + 32; - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if (bx <= dpi->y) return; dx -= 20; @@ -136,7 +136,7 @@ bool gShowSupportSegmentHeights = false; */ static void sub_68B3FB(paint_session* session, int32_t x, int32_t y) { - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if ((session->ViewFlags & VIEWPORT_FLAG_CLIP_VIEW)) { diff --git a/src/openrct2/ride/TrackPaint.cpp b/src/openrct2/ride/TrackPaint.cpp index 7e839aa8c0..b95f39c0e3 100644 --- a/src/openrct2/ride/TrackPaint.cpp +++ b/src/openrct2/ride/TrackPaint.cpp @@ -2164,7 +2164,7 @@ void track_paint(paint_session* session, uint8_t direction, int32_t height, cons return; } - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if ((!gTrackDesignSaveMode || rideIndex == gTrackDesignSaveRideIndex) && !(session->ViewFlags & VIEWPORT_FLAG_HIGHLIGHT_PATH_ISSUES)) diff --git a/src/openrct2/ride/VehiclePaint.cpp b/src/openrct2/ride/VehiclePaint.cpp index 20016dd01f..e1d91f78ac 100644 --- a/src/openrct2/ride/VehiclePaint.cpp +++ b/src/openrct2/ride/VehiclePaint.cpp @@ -915,7 +915,7 @@ static void vehicle_sprite_paint( { ps->tertiary_colour = vehicle->colours_extended; } - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if (dpi->zoom_level < 2 && vehicle->num_peeps > 0 && vehicleEntry->no_seating_rows > 0) { baseImage_id += vehicleEntry->no_vehicle_images; diff --git a/src/openrct2/ride/coaster/VirginiaReel.cpp b/src/openrct2/ride/coaster/VirginiaReel.cpp index 68370e32de..3336da22c3 100644 --- a/src/openrct2/ride/coaster/VirginiaReel.cpp +++ b/src/openrct2/ride/coaster/VirginiaReel.cpp @@ -199,7 +199,7 @@ void vehicle_visual_virginia_reel( sub_98197C( session, image_id, 0, 0, bb->length_x, bb->length_y, bb->length_z, z, bb->offset_x, bb->offset_y, bb->offset_z + z); - if (session->DPI->zoom_level < 2 && vehicle->num_peeps > 0) + if (session->DPI.zoom_level < 2 && vehicle->num_peeps > 0) { uint8_t riding_peep_sprites[4] = { 0xFF, 0xFF, 0xFF, 0xFF }; for (int32_t i = 0; i < vehicle->num_peeps; i++) diff --git a/src/openrct2/ride/gentle/HauntedHouse.cpp b/src/openrct2/ride/gentle/HauntedHouse.cpp index 568021a629..569e10faf0 100644 --- a/src/openrct2/ride/gentle/HauntedHouse.cpp +++ b/src/openrct2/ride/gentle/HauntedHouse.cpp @@ -62,7 +62,7 @@ static void paint_haunted_house_structure( session, imageId, xOffset, yOffset, boundBox.length_x, boundBox.length_y, 127, height, boundBox.offset_x, boundBox.offset_y, height); - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if (dpi->zoom_level == 0 && frameNum != 0) { switch (direction) diff --git a/src/openrct2/ride/gentle/MerryGoRound.cpp b/src/openrct2/ride/gentle/MerryGoRound.cpp index 3b4ff34d4d..fb3db1cc96 100644 --- a/src/openrct2/ride/gentle/MerryGoRound.cpp +++ b/src/openrct2/ride/gentle/MerryGoRound.cpp @@ -71,7 +71,7 @@ static void paint_merry_go_round_structure( uint32_t imageId = (baseImageId + imageOffset) | imageColourFlags; sub_98197C(session, imageId, xOffset, yOffset, 24, 24, 48, height, xOffset + 16, yOffset + 16, height); - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if (dpi->zoom_level == 0 && ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK && vehicle != nullptr) { for (int32_t peep = 0; peep <= 14; peep += 2) diff --git a/src/openrct2/ride/gentle/MiniGolf.cpp b/src/openrct2/ride/gentle/MiniGolf.cpp index febe16ec0e..4c0152393e 100644 --- a/src/openrct2/ride/gentle/MiniGolf.cpp +++ b/src/openrct2/ride/gentle/MiniGolf.cpp @@ -1193,7 +1193,7 @@ void vehicle_visual_mini_golf_player( return; } - rct_drawpixelinfo* edi = session->DPI; + rct_drawpixelinfo* edi = &session->DPI; if (edi->zoom_level >= 2) { return; @@ -1226,7 +1226,7 @@ void vehicle_visual_mini_golf_ball( return; } - rct_drawpixelinfo* edi = session->DPI; + rct_drawpixelinfo* edi = &session->DPI; if (edi->zoom_level >= 1) { return; diff --git a/src/openrct2/ride/gentle/SpiralSlide.cpp b/src/openrct2/ride/gentle/SpiralSlide.cpp index 693351001b..4101cb5e96 100644 --- a/src/openrct2/ride/gentle/SpiralSlide.cpp +++ b/src/openrct2/ride/gentle/SpiralSlide.cpp @@ -121,7 +121,7 @@ static void spiral_slide_paint_tile_front( sub_98197C(session, image_id, 16, 16, 8, 16, 108, height, 8, 0, height + 3); } - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if (dpi->zoom_level == 0 && ride->slide_in_use != 0) { uint8_t slide_progress = ride->spiral_slide_progress; diff --git a/src/openrct2/ride/thrill/Enterprise.cpp b/src/openrct2/ride/thrill/Enterprise.cpp index 2ee2a68f88..0a8cb47c50 100644 --- a/src/openrct2/ride/thrill/Enterprise.cpp +++ b/src/openrct2/ride/thrill/Enterprise.cpp @@ -54,7 +54,7 @@ static void paint_enterprise_structure( uint32_t imageId = (baseImageId + imageOffset) | imageColourFlags; sub_98197C(session, imageId, xOffset, yOffset, 24, 24, 48, height, 0, 0, height); - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if (dpi->zoom_level == 0 && imageOffset < 12 && ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK && vehicle != nullptr) { diff --git a/src/openrct2/ride/thrill/LaunchedFreefall.cpp b/src/openrct2/ride/thrill/LaunchedFreefall.cpp index d516982cbf..39e9aa980e 100644 --- a/src/openrct2/ride/thrill/LaunchedFreefall.cpp +++ b/src/openrct2/ride/thrill/LaunchedFreefall.cpp @@ -47,7 +47,7 @@ void vehicle_visual_launched_freefall( sub_98197C(session, image_id, 0, 0, 16, 16, 41, z, -5, -5, z + 1); // Draw peeps: - if (session->DPI->zoom_level < 2) + if (session->DPI.zoom_level < 2) { if (vehicle->num_peeps > 0) { diff --git a/src/openrct2/ride/thrill/MagicCarpet.cpp b/src/openrct2/ride/thrill/MagicCarpet.cpp index 8d4673fa44..390ebdfdf2 100644 --- a/src/openrct2/ride/thrill/MagicCarpet.cpp +++ b/src/openrct2/ride/thrill/MagicCarpet.cpp @@ -155,7 +155,7 @@ static void paint_magic_carpet_vehicle( bbOffset.x, bbOffset.y, bbOffset.z); // Riders - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if (dpi->zoom_level <= 1 && (ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK)) { rct_vehicle* vehicle = get_first_vehicle(ride); diff --git a/src/openrct2/ride/thrill/PirateShip.cpp b/src/openrct2/ride/thrill/PirateShip.cpp index b9c22faf2e..4c20195a2a 100644 --- a/src/openrct2/ride/thrill/PirateShip.cpp +++ b/src/openrct2/ride/thrill/PirateShip.cpp @@ -115,7 +115,7 @@ static void paint_pirate_ship_structure( session, imageId, xOffset, yOffset, bounds.length_x, bounds.length_y, 80, height, bounds.offset_x, bounds.offset_y, height); - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if (dpi->zoom_level <= 1 && ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK && vehicle != nullptr) { diff --git a/src/openrct2/ride/thrill/TopSpin.cpp b/src/openrct2/ride/thrill/TopSpin.cpp index 3c65e3ff37..f3527ce196 100644 --- a/src/openrct2/ride/thrill/TopSpin.cpp +++ b/src/openrct2/ride/thrill/TopSpin.cpp @@ -167,7 +167,7 @@ static void top_spin_paint_vehicle( session, image_id, (int8_t)seatCoords.x, (int8_t)seatCoords.y, lengthX, lengthY, 90, seatCoords.z, boundBoxOffsetX, boundBoxOffsetY, boundBoxOffsetZ); - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if (dpi->zoom_level < 2 && vehicle != nullptr && vehicle->num_peeps != 0) { image_id = (seatImageId + (1 * 76)) diff --git a/src/openrct2/ride/thrill/Twist.cpp b/src/openrct2/ride/thrill/Twist.cpp index 53c2925586..a3e0ac979a 100644 --- a/src/openrct2/ride/thrill/Twist.cpp +++ b/src/openrct2/ride/thrill/Twist.cpp @@ -58,7 +58,7 @@ static void paint_twist_structure( uint32_t imageId = (baseImageId + structureFrameNum) | imageColourFlags; sub_98197C(session, imageId, xOffset, yOffset, 24, 24, 48, height, xOffset + 16, yOffset + 16, height); - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if (dpi->zoom_level < 1 && ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK && vehicle != nullptr) { diff --git a/src/openrct2/ride/water/RiverRapids.cpp b/src/openrct2/ride/water/RiverRapids.cpp index 1e72267cb0..b6886dc5bf 100644 --- a/src/openrct2/ride/water/RiverRapids.cpp +++ b/src/openrct2/ride/water/RiverRapids.cpp @@ -226,7 +226,7 @@ void vehicle_visual_river_rapids( sub_98197C( session, image_id, 0, 0, bb->length_x, bb->length_y, bb->length_z, z, bb->offset_x, bb->offset_y, bb->offset_z + z); - if (session->DPI->zoom_level < 2 && vehicle->num_peeps > 0) + if (session->DPI.zoom_level < 2 && vehicle->num_peeps > 0) { // Draw peeps: (this particular vehicle doesn't sort them back to front like others so the back ones sometimes clip, but // that's how the original does it...) diff --git a/test/testpaint/TestPaint.cpp b/test/testpaint/TestPaint.cpp index 97c72195d5..a78a808f31 100644 --- a/test/testpaint/TestPaint.cpp +++ b/test/testpaint/TestPaint.cpp @@ -43,7 +43,7 @@ namespace TestPaint rct_drawpixelinfo dpi = {}; dpi.zoom_level = 1; RCT2_Unk140E9A8 = &dpi; - gPaintSession.DPI = &dpi; + gPaintSession.DPI = dpi; { Ride ride = {};