From 970fedd78cef3f5ef7a26fcaf4fd9db0f6abbe4b Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Tue, 9 Mar 2021 14:53:51 +0100 Subject: [PATCH] Add: make modal windows update more smooth Basically, modal windows had their own thread-locking for what drawing was possible. This is a bit nonsense now we have a game-thread. And it makes much more sense to do things like NewGRFScan and GenerateWorld in the game-thread, and not in a thread next to the game-thread. This commit changes that: it removes the threads for NewGRFScan and GenerateWorld, and just runs the code in the game-thread. On regular intervals it allows the draw-thread to do a tick, which gives a much smoother look and feel. It does slow down NewGRFScan and GenerateWorld ever so slightly as it spends more time on drawing. But the slowdown is not measureable on my machines (with 700+ NewGRFs / 4kx4k map and a Debug build). Running without a game-thread means NewGRFScan and GenerateWorld are now blocking. --- src/clear_cmd.cpp | 1 + src/console_cmds.cpp | 2 +- src/genworld.cpp | 80 ++++++----------------------- src/genworld.h | 5 -- src/genworld_gui.cpp | 24 +++------ src/gfx.cpp | 20 -------- src/landscape.cpp | 6 +++ src/network/network_content_gui.cpp | 3 +- src/newgrf_config.cpp | 33 +++--------- src/newgrf_gui.cpp | 2 +- src/openttd.cpp | 40 ++++++++------- src/openttd.h | 2 + src/progress.cpp | 21 -------- src/progress.h | 18 ------- src/thread.h | 5 -- src/tree_cmd.cpp | 1 + src/video/dedicated_v.cpp | 14 ++--- src/video/video_driver.cpp | 19 +++++++ src/video/video_driver.hpp | 2 + 19 files changed, 89 insertions(+), 209 deletions(-) diff --git a/src/clear_cmd.cpp b/src/clear_cmd.cpp index 6d3167e5a2..4036313123 100644 --- a/src/clear_cmd.cpp +++ b/src/clear_cmd.cpp @@ -338,6 +338,7 @@ void GenerateClearTile() TileIndex tile_new; SetClearGroundDensity(tile, CLEAR_ROCKS, 3); + MarkTileDirtyByTile(tile); do { if (--j == 0) goto get_out; tile_new = tile + TileOffsByDiagDir((DiagDirection)GB(Random(), 0, 2)); diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index 8a7a5e399e..18c4263f5d 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -1340,7 +1340,7 @@ DEF_CONSOLE_CMD(ConRescanNewGRF) return true; } - ScanNewGRFFiles(nullptr); + RequestNewGRFScan(); return true; } diff --git a/src/genworld.cpp b/src/genworld.cpp index c76fe309d9..03fa08819b 100644 --- a/src/genworld.cpp +++ b/src/genworld.cpp @@ -59,18 +59,10 @@ GenWorldInfo _gw; /** Whether we are generating the map or not. */ bool _generating_world; -/** - * Tells if the world generation is done in a thread or not. - * @return the 'threaded' status - */ -bool IsGenerateWorldThreaded() -{ - return _gw.threaded && !_gw.quit_thread; -} +class AbortGenerateWorldSignal { }; /** - * Clean up the 'mess' of generation. That is, show windows again, reset - * thread variables, and delete the progress window. + * Generation is done; show windows again and delete the progress window. */ static void CleanupGeneration() { @@ -78,11 +70,10 @@ static void CleanupGeneration() SetMouseCursorBusy(false); /* Show all vital windows again, because we have hidden them */ - if (_gw.threaded && _game_mode != GM_MENU) ShowVitalWindows(); + if (_game_mode != GM_MENU) ShowVitalWindows(); SetModalProgress(false); _gw.proc = nullptr; _gw.abortp = nullptr; - _gw.threaded = false; DeleteWindowByClass(WC_MODAL_PROGRESS); ShowFirstError(); @@ -97,10 +88,8 @@ static void _GenerateWorld() /* Make sure everything is done via OWNER_NONE. */ Backup _cur_company(_current_company, OWNER_NONE, FILE_LINE); - std::unique_lock lock(_modal_progress_work_mutex, std::defer_lock); try { _generating_world = true; - lock.lock(); if (_network_dedicated) DEBUG(net, 1, "Generating map, please wait..."); /* Set the Random() seed to generation_seed so we produce the same map with the same seed */ if (_settings_game.game_creation.generation_seed == GENERATE_NEW_SEED) _settings_game.game_creation.generation_seed = _settings_newgame.game_creation.generation_seed = InteractiveRandom(); @@ -136,14 +125,7 @@ static void _GenerateWorld() /* Only generate towns, tree and industries in newgame mode. */ if (_game_mode != GM_EDITOR) { if (!GenerateTowns(_settings_game.economy.town_layout)) { - _cur_company.Restore(); HandleGeneratingWorldAbortion(); - BasePersistentStorageArray::SwitchMode(PSM_LEAVE_GAMELOOP); - if (_network_dedicated) { - /* Exit the game to prevent a return to main menu. */ - DEBUG(net, 0, "Generating map failed, aborting"); - _exit_game = true; - } return; } GenerateIndustries(); @@ -200,7 +182,6 @@ static void _GenerateWorld() IncreaseGeneratingWorldProgress(GWP_GAME_START); CleanupGeneration(); - lock.unlock(); ShowNewGRFError(); @@ -212,11 +193,19 @@ static void _GenerateWorld() seprintf(name, lastof(name), "dmp_cmds_%08x_%08x.sav", _settings_game.game_creation.generation_seed, _date); SaveOrLoad(name, SLO_SAVE, DFT_GAME_FILE, AUTOSAVE_DIR, false); } - } catch (...) { + } catch (AbortGenerateWorldSignal&) { + CleanupGeneration(); + BasePersistentStorageArray::SwitchMode(PSM_LEAVE_GAMELOOP, true); if (_cur_company.IsValid()) _cur_company.Restore(); - _generating_world = false; - throw; + + if (_network_dedicated) { + /* Exit the game to prevent a return to main menu. */ + DEBUG(net, 0, "Generating map failed, aborting"); + _exit_game = true; + } else { + SwitchToMode(_switch_mode); + } } } @@ -240,23 +229,6 @@ void GenerateWorldSetAbortCallback(GWAbortProc *proc) _gw.abortp = proc; } -/** - * This will wait for the thread to finish up his work. It will not continue - * till the work is done. - */ -void WaitTillGeneratedWorld() -{ - if (!_gw.thread.joinable()) return; - - _modal_progress_work_mutex.unlock(); - _modal_progress_paint_mutex.unlock(); - _gw.quit_thread = true; - _gw.thread.join(); - _gw.threaded = false; - _modal_progress_work_mutex.lock(); - _modal_progress_paint_mutex.lock(); -} - /** * Initializes the abortion process */ @@ -284,11 +256,7 @@ void HandleGeneratingWorldAbortion() if (_gw.abortp != nullptr) _gw.abortp(); - CleanupGeneration(); - - if (_gw.thread.joinable() && _gw.thread.get_id() == std::this_thread::get_id()) throw OTTDThreadExitSignal(); - - SwitchToMode(_switch_mode); + throw AbortGenerateWorldSignal(); } /** @@ -308,8 +276,6 @@ void GenerateWorld(GenWorldMode mode, uint size_x, uint size_y, bool reset_setti _gw.abort = false; _gw.abortp = nullptr; _gw.lc = _local_company; - _gw.quit_thread = false; - _gw.threaded = true; /* This disables some commands and stuff */ SetLocalCompany(COMPANY_SPECTATOR); @@ -328,28 +294,16 @@ void GenerateWorld(GenWorldMode mode, uint size_x, uint size_y, bool reset_setti SetupColoursAndInitialWindow(); SetObjectToPlace(SPR_CURSOR_ZZZ, PAL_NONE, HT_NONE, WC_MAIN_WINDOW, 0); - if (_gw.thread.joinable()) _gw.thread.join(); - - if (!UseThreadedModelProgress() || !VideoDriver::GetInstance()->HasGUI() || !StartNewThread(&_gw.thread, "ottd:genworld", &_GenerateWorld)) { - DEBUG(misc, 1, "Cannot create genworld thread, reverting to single-threaded mode"); - _gw.threaded = false; - _modal_progress_work_mutex.unlock(); - _GenerateWorld(); - _modal_progress_work_mutex.lock(); - return; - } - UnshowCriticalError(); - /* Remove any open window */ DeleteAllNonVitalWindows(); - /* Hide vital windows, because we don't allow to use them */ HideVitalWindows(); - /* Don't show the dialog if we don't have a thread */ ShowGenerateWorldProgress(); /* Centre the view on the map */ if (FindWindowById(WC_MAIN_WINDOW, 0) != nullptr) { ScrollMainWindowToTile(TileXY(MapSizeX() / 2, MapSizeY() / 2), true); } + + _GenerateWorld(); } diff --git a/src/genworld.h b/src/genworld.h index 8014797125..e780ed1694 100644 --- a/src/genworld.h +++ b/src/genworld.h @@ -52,15 +52,12 @@ typedef void GWAbortProc(); ///< Called when genworld is aborted /** Properties of current genworld process */ struct GenWorldInfo { bool abort; ///< Whether to abort the thread ASAP - bool quit_thread; ///< Do we want to quit the active thread - bool threaded; ///< Whether we run _GenerateWorld threaded GenWorldMode mode; ///< What mode are we making a world in CompanyID lc; ///< The local_company before generating uint size_x; ///< X-size of the map uint size_y; ///< Y-size of the map GWDoneProc *proc; ///< Proc that is called when done (can be nullptr) GWAbortProc *abortp; ///< Proc that is called when aborting (can be nullptr) - std::thread thread; ///< The thread we are in (joinable if a thread was created) }; /** Current stage of world generation process */ @@ -81,10 +78,8 @@ enum GenWorldProgress { }; /* genworld.cpp */ -bool IsGenerateWorldThreaded(); void GenerateWorldSetCallback(GWDoneProc *proc); void GenerateWorldSetAbortCallback(GWAbortProc *proc); -void WaitTillGeneratedWorld(); void GenerateWorld(GenWorldMode mode, uint size_x, uint size_y, bool reset_settings = true); void AbortGeneratingWorld(); bool IsGeneratingWorldAborted(); diff --git a/src/genworld_gui.cpp b/src/genworld_gui.cpp index bf4d327a7e..f26d43ac39 100644 --- a/src/genworld_gui.cpp +++ b/src/genworld_gui.cpp @@ -29,6 +29,7 @@ #include "error.h" #include "newgrf_townname.h" #include "townname_type.h" +#include "video/video_driver.hpp" #include "widgets/genworld_widget.h" @@ -1312,10 +1313,10 @@ static void _SetGeneratingWorldProgress(GenWorldProgress cls, uint progress, uin static_assert(lengthof(percent_table) == GWP_CLASS_COUNT + 1); assert(cls < GWP_CLASS_COUNT); - /* Do not run this function if we aren't in a thread */ - if (!IsGenerateWorldThreaded() && !_network_dedicated) return; - - if (IsGeneratingWorldAborted()) HandleGeneratingWorldAbortion(); + if (IsGeneratingWorldAborted()) { + HandleGeneratingWorldAbortion(); + return; + } if (total == 0) { assert(_gws.cls == _generation_class_table[cls]); @@ -1328,10 +1329,6 @@ static void _SetGeneratingWorldProgress(GenWorldProgress cls, uint progress, uin _gws.percent = percent_table[cls]; } - /* Don't update the screen too often. So update it once in every once in a while... */ - if (!_network_dedicated && std::chrono::steady_clock::now() < _gws.next_update) return; - _gws.next_update = std::chrono::steady_clock::now() + std::chrono::milliseconds(MODAL_PROGRESS_REDRAW_TIMEOUT); - /* Percentage is about the number of completed tasks, so 'current - 1' */ _gws.percent = percent_table[cls] + (percent_table[cls + 1] - percent_table[cls]) * (_gws.current == 0 ? 0 : _gws.current - 1) / _gws.total; @@ -1350,21 +1347,12 @@ static void _SetGeneratingWorldProgress(GenWorldProgress cls, uint progress, uin DEBUG(net, 1, "Map generation percentage complete: %d", _gws.percent); last_percent = _gws.percent; - /* Don't continue as dedicated never has a thread running */ return; } SetWindowDirty(WC_MODAL_PROGRESS, 0); - MarkWholeScreenDirty(); - /* Release the rights to the map generator, and acquire the rights to the - * paint thread. The 'other' thread already has the paint thread rights so - * this ensures us that we are waiting until the paint thread is done - * before we reacquire the mapgen rights */ - _modal_progress_work_mutex.unlock(); - _modal_progress_paint_mutex.lock(); - _modal_progress_work_mutex.lock(); - _modal_progress_paint_mutex.unlock(); + VideoDriver::GetInstance()->GameLoopPause(); } /** diff --git a/src/gfx.cpp b/src/gfx.cpp index 3189b8d96b..6bcee66b1b 100644 --- a/src/gfx.cpp +++ b/src/gfx.cpp @@ -1470,26 +1470,6 @@ void DrawDirtyBlocks() int x; int y; - if (HasModalProgress()) { - /* We are generating the world, so release our rights to the map and - * painting while we are waiting a bit. */ - _modal_progress_paint_mutex.unlock(); - _modal_progress_work_mutex.unlock(); - - /* Wait a while and hope the modal gives us a bit of time to draw the GUI. */ - if (!IsFirstModalProgressLoop()) CSleep(MODAL_PROGRESS_REDRAW_TIMEOUT); - - /* Modal progress thread may need blitter access while we are waiting for it. */ - _modal_progress_paint_mutex.lock(); - _modal_progress_work_mutex.lock(); - - /* When we ended with the modal progress, do not draw the blocks. - * Simply let the next run do so, otherwise we would be loading - * the new state (and possibly change the blitter) when we hold - * the drawing lock, which we must not do. */ - if (_switch_mode != SM_NONE && !HasModalProgress()) return; - } - y = 0; do { x = 0; diff --git a/src/landscape.cpp b/src/landscape.cpp index 80ba516ac7..ab6ba59f0c 100644 --- a/src/landscape.cpp +++ b/src/landscape.cpp @@ -1062,6 +1062,7 @@ static bool MakeLake(TileIndex tile, void *user_data) TileIndex t2 = tile + TileOffsByDiagDir(d); if (IsWaterTile(t2)) { MakeRiver(tile, Random()); + MarkTileDirtyByTile(tile); /* Remove desert directly around the river tile. */ TileIndex t = tile; CircularTileSearch(&t, RIVER_OFFSET_DESERT_DISTANCE, RiverModifyDesertZone, nullptr); @@ -1135,6 +1136,7 @@ static void River_FoundEndNode(AyStar *aystar, OpenListNode *current) TileIndex tile = path->node.tile; if (!IsWaterTile(tile)) { MakeRiver(tile, Random()); + MarkTileDirtyByTile(tile); /* Remove desert directly around the river tile. */ CircularTileSearch(&tile, RIVER_OFFSET_DESERT_DISTANCE, RiverModifyDesertZone, nullptr); } @@ -1247,6 +1249,7 @@ static bool FlowRiver(TileIndex spring, TileIndex begin) DistanceManhattan(spring, lakeCenter) > _settings_game.game_creation.min_river_length) { end = lakeCenter; MakeRiver(lakeCenter, Random()); + MarkTileDirtyByTile(lakeCenter); /* Remove desert directly around the river tile. */ CircularTileSearch(&lakeCenter, RIVER_OFFSET_DESERT_DISTANCE, RiverModifyDesertZone, nullptr); lakeCenter = end; @@ -1368,8 +1371,11 @@ void GenerateLandscape(byte mode) /* Do not call IncreaseGeneratingWorldProgress() before FixSlopes(), * it allows screen redraw. Drawing of broken slopes crashes the game */ FixSlopes(); + MarkWholeScreenDirty(); IncreaseGeneratingWorldProgress(GWP_LANDSCAPE); + ConvertGroundTilesIntoWaterTiles(); + MarkWholeScreenDirty(); IncreaseGeneratingWorldProgress(GWP_LANDSCAPE); if (_settings_game.game_creation.landscape == LT_TROPIC) CreateDesertOrRainForest(); diff --git a/src/network/network_content_gui.cpp b/src/network/network_content_gui.cpp index 13e162b07a..4662791257 100644 --- a/src/network/network_content_gui.cpp +++ b/src/network/network_content_gui.cpp @@ -15,6 +15,7 @@ #include "../ai/ai.hpp" #include "../game/game.hpp" #include "../base_media_base.h" +#include "../openttd.h" #include "../sortlist_type.h" #include "../stringfilter_type.h" #include "../querystring_gui.h" @@ -236,7 +237,7 @@ public: break; case CONTENT_TYPE_NEWGRF: - ScanNewGRFFiles(nullptr); + RequestNewGRFScan(); break; case CONTENT_TYPE_SCENARIO: diff --git a/src/newgrf_config.cpp b/src/newgrf_config.cpp index e56f878bdb..911d266df0 100644 --- a/src/newgrf_config.cpp +++ b/src/newgrf_config.cpp @@ -635,20 +635,12 @@ bool GRFFileScanner::AddFile(const std::string &filename, size_t basepath_length } this->num_scanned++; - if (std::chrono::steady_clock::now() >= this->next_update) { - this->next_update = std::chrono::steady_clock::now() + std::chrono::milliseconds(MODAL_PROGRESS_REDRAW_TIMEOUT); - _modal_progress_work_mutex.unlock(); - _modal_progress_paint_mutex.lock(); - - const char *name = nullptr; - if (c->name != nullptr) name = GetGRFStringFromGRFText(c->name); - if (name == nullptr) name = c->filename; - UpdateNewGRFScanStatus(this->num_scanned, name); - - _modal_progress_work_mutex.lock(); - _modal_progress_paint_mutex.unlock(); - } + const char *name = nullptr; + if (c->name != nullptr) name = GetGRFStringFromGRFText(c->name); + if (name == nullptr) name = c->filename; + UpdateNewGRFScanStatus(this->num_scanned, name); + VideoDriver::GetInstance()->GameLoopPause(); if (!added) { /* File couldn't be opened, or is either not a NewGRF or is a @@ -676,8 +668,6 @@ static bool GRFSorter(GRFConfig * const &c1, GRFConfig * const &c2) */ void DoScanNewGRFFiles(NewGRFScanCallback *callback) { - std::unique_lock lock_work(_modal_progress_work_mutex); - ClearGRFConfigList(&_all_grfs); TarScanner::DoScan(TarScanner::NEWGRF); @@ -709,9 +699,6 @@ void DoScanNewGRFFiles(NewGRFScanCallback *callback) NetworkAfterNewGRFScan(); } - lock_work.unlock(); - std::lock_guard lock_paint(_modal_progress_paint_mutex); - /* Yes... these are the NewGRF windows */ InvalidateWindowClassesData(WC_SAVELOAD, 0, true); InvalidateWindowData(WC_GAME_OPTIONS, WN_GAME_OPTIONS_NEWGRF_STATE, GOID_NEWGRF_RESCANNED, true); @@ -733,15 +720,7 @@ void ScanNewGRFFiles(NewGRFScanCallback *callback) /* Only then can we really start, especially by marking the whole screen dirty. Get those other windows hidden!. */ MarkWholeScreenDirty(); - if (!UseThreadedModelProgress() || !VideoDriver::GetInstance()->HasGUI() || !StartNewThread(nullptr, "ottd:newgrf-scan", &DoScanNewGRFFiles, (NewGRFScanCallback *)callback)) { // Without the seemingly superfluous cast, strange compiler errors ensue. - _modal_progress_work_mutex.unlock(); - _modal_progress_paint_mutex.unlock(); - DoScanNewGRFFiles(callback); - _modal_progress_paint_mutex.lock(); - _modal_progress_work_mutex.lock(); - } else { - UpdateNewGRFScanStatus(0, nullptr); - } + DoScanNewGRFFiles(callback); } /** diff --git a/src/newgrf_gui.cpp b/src/newgrf_gui.cpp index 9e340654c5..4e63eafbe6 100644 --- a/src/newgrf_gui.cpp +++ b/src/newgrf_gui.cpp @@ -1127,7 +1127,7 @@ struct NewGRFWindow : public Window, NewGRFScanCallback { case WID_NS_RESCAN_FILES: case WID_NS_RESCAN_FILES2: - ScanNewGRFFiles(this); + RequestNewGRFScan(this); break; } } diff --git a/src/openttd.cpp b/src/openttd.cpp index 3397e4f0e0..2d37c8e3a0 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -91,6 +91,8 @@ extern void ShowOSErrorBox(const char *buf, bool system); extern std::string _config_file; bool _save_config = false; +bool _request_newgrf_scan = false; +NewGRFScanCallback *_request_newgrf_scan_callback = nullptr; /** * Error handling for fatal user errors. @@ -345,7 +347,6 @@ static void LoadIntroGame(bool load_newgrfs = true) /* Load the default opening screen savegame */ if (SaveOrLoad("opntitle.dat", SLO_LOAD, DFT_GAME_FILE, BASESET_DIR) != SL_OK) { GenerateWorld(GWM_EMPTY, 64, 64); // if failed loading, make empty world. - WaitTillGeneratedWorld(); SetLocalCompany(COMPANY_SPECTATOR); } else { SetLocalCompany(COMPANY_FIRST); @@ -559,9 +560,6 @@ int openttd_main(int argc, char *argv[]) extern bool _dedicated_forks; _dedicated_forks = false; - std::unique_lock modal_work_lock(_modal_progress_work_mutex, std::defer_lock); - std::unique_lock modal_paint_lock(_modal_progress_paint_mutex, std::defer_lock); - _game_mode = GM_MENU; _switch_mode = SM_MENU; @@ -828,30 +826,17 @@ int openttd_main(int argc, char *argv[]) if (musicdriver.empty() && !_ini_musicdriver.empty()) musicdriver = _ini_musicdriver; DriverFactoryBase::SelectDriver(musicdriver, Driver::DT_MUSIC); - /* Take our initial lock on whatever we might want to do! */ - try { - modal_work_lock.lock(); - modal_paint_lock.lock(); - } catch (const std::system_error&) { - /* If there is some error we assume that threads aren't usable on the system we run. */ - extern bool _use_threaded_modal_progress; // From progress.cpp - _use_threaded_modal_progress = false; - } - GenerateWorld(GWM_EMPTY, 64, 64); // Make the viewport initialization happy - WaitTillGeneratedWorld(); - LoadIntroGame(false); CheckForMissingGlyphs(); /* ScanNewGRFFiles now has control over the scanner. */ - ScanNewGRFFiles(scanner.release()); + RequestNewGRFScan(scanner.release()); VideoDriver::GetInstance()->MainLoop(); WaitTillSaved(); - WaitTillGeneratedWorld(); // Make sure any generate world threads have been joined. /* only save config if we have to */ if (_save_config) { @@ -1460,6 +1445,19 @@ static void DoAutosave() } } +/** + * Request a new NewGRF scan. This will be executed on the next game-tick. + * This is mostly needed to ensure NewGRF scans (which are blocking) are + * done in the game-thread, and not in the draw-thread (which most often + * triggers this request). + * @param callback Optional callback to call when NewGRF scan is completed. + */ +void RequestNewGRFScan(NewGRFScanCallback *callback) +{ + _request_newgrf_scan = true; + _request_newgrf_scan_callback = callback; +} + void GameLoop() { if (_game_mode == GM_BOOTSTRAP) { @@ -1468,6 +1466,12 @@ void GameLoop() return; } + if (_request_newgrf_scan) { + ScanNewGRFFiles(_request_newgrf_scan_callback); + _request_newgrf_scan = false; + _request_newgrf_scan_callback = nullptr; + } + ProcessAsyncSaveFinish(); /* autosave game? */ diff --git a/src/openttd.h b/src/openttd.h index 62651fe7f0..38c7f80644 100644 --- a/src/openttd.h +++ b/src/openttd.h @@ -81,4 +81,6 @@ void HandleExitGameRequest(); void SwitchToMode(SwitchMode new_mode); +void RequestNewGRFScan(struct NewGRFScanCallback *callback = nullptr); + #endif /* OPENTTD_H */ diff --git a/src/progress.cpp b/src/progress.cpp index e45e5db078..6444d8a85f 100644 --- a/src/progress.cpp +++ b/src/progress.cpp @@ -14,33 +14,12 @@ /** Are we in a modal progress or not? */ bool _in_modal_progress = false; -bool _first_in_modal_loop = false; -/** Threading usable for modal progress? */ -bool _use_threaded_modal_progress = true; -/** Rights for the performing work. */ -std::mutex _modal_progress_work_mutex; -/** Rights for the painting. */ -std::mutex _modal_progress_paint_mutex; /** * Set the modal progress state. - * @note Makes IsFirstModalProgressLoop return true for the next call. * @param state The new state; are we modal or not? */ void SetModalProgress(bool state) { _in_modal_progress = state; - _first_in_modal_loop = true; -} - -/** - * Check whether this is the first modal progress loop. - * @note Set by SetModalProgress, unset by calling this method. - * @return True if this is the first loop. - */ -bool IsFirstModalProgressLoop() -{ - bool ret = _first_in_modal_loop; - _first_in_modal_loop = false; - return ret; } diff --git a/src/progress.h b/src/progress.h index 59a61c678e..765020635e 100644 --- a/src/progress.h +++ b/src/progress.h @@ -10,10 +10,6 @@ #ifndef PROGRESS_H #define PROGRESS_H -#include - -static const uint MODAL_PROGRESS_REDRAW_TIMEOUT = 200; ///< Timeout between redraws - /** * Check if we are currently in a modal progress state. * @return Are we in the modal state? @@ -24,20 +20,6 @@ static inline bool HasModalProgress() return _in_modal_progress; } -/** - * Check if we can use a thread for modal progress. - * @return Threading usable? - */ -static inline bool UseThreadedModelProgress() -{ - extern bool _use_threaded_modal_progress; - return _use_threaded_modal_progress; -} - -bool IsFirstModalProgressLoop(); void SetModalProgress(bool state); -extern std::mutex _modal_progress_work_mutex; -extern std::mutex _modal_progress_paint_mutex; - #endif /* PROGRESS_H */ diff --git a/src/thread.h b/src/thread.h index f4a16d4e0d..f45694930b 100644 --- a/src/thread.h +++ b/src/thread.h @@ -14,10 +14,6 @@ #include #include -/** Signal used for signalling we knowingly want to end the thread. */ -class OTTDThreadExitSignal { }; - - /** * Sleep on the current thread for a defined time. * @param milliseconds Time to sleep for in milliseconds. @@ -54,7 +50,6 @@ inline bool StartNewThread(std::thread *thr, const char *name, TFn&& _Fx, TArgs& try { /* Call user function with the given arguments. */ F(A...); - } catch (OTTDThreadExitSignal&) { } catch (...) { NOT_REACHED(); } diff --git a/src/tree_cmd.cpp b/src/tree_cmd.cpp index e4ce1da142..c0865ffc68 100644 --- a/src/tree_cmd.cpp +++ b/src/tree_cmd.cpp @@ -164,6 +164,7 @@ static void PlaceTree(TileIndex tile, uint32 r) if (tree != TREE_INVALID) { PlantTreesOnTile(tile, tree, GB(r, 22, 2), std::min(GB(r, 16, 3), 6)); + MarkTileDirtyByTile(tile); /* Rerandomize ground, if neither snow nor shore */ TreeGround ground = GetTreeGround(tile); diff --git a/src/video/dedicated_v.cpp b/src/video/dedicated_v.cpp index ea1c14cff7..e905a9d2c9 100644 --- a/src/video/dedicated_v.cpp +++ b/src/video/dedicated_v.cpp @@ -251,19 +251,16 @@ void VideoDriver_Dedicated::MainLoop() /* If SwitchMode is SM_LOAD_GAME, it means that the user used the '-g' options */ if (_switch_mode != SM_LOAD_GAME) { StartNewGameWithoutGUI(GENERATE_NEW_SEED); - SwitchToMode(_switch_mode); - _switch_mode = SM_NONE; } else { - _switch_mode = SM_NONE; /* First we need to test if the savegame can be loaded, else we will end up playing the * intro game... */ - if (!SafeLoad(_file_to_saveload.name, _file_to_saveload.file_op, _file_to_saveload.detail_ftype, GM_NORMAL, BASE_DIR)) { + if (SaveOrLoad(_file_to_saveload.name, _file_to_saveload.file_op, _file_to_saveload.detail_ftype, BASE_DIR) == SL_ERROR) { /* Loading failed, pop out.. */ DEBUG(net, 0, "Loading requested map failed, aborting"); - _networking = false; + return; } else { /* We can load this game, so go ahead */ - SwitchToMode(SM_LOAD_GAME); + _switch_mode = SM_LOAD_GAME; } } @@ -271,11 +268,6 @@ void VideoDriver_Dedicated::MainLoop() /* Done loading, start game! */ - if (!_networking) { - DEBUG(net, 0, "Dedicated server could not be started, aborting"); - return; - } - while (!_exit_game) { if (!_dedicated_forks) DedicatedHandleKeyInput(); diff --git a/src/video/video_driver.cpp b/src/video/video_driver.cpp index 1b7fc6a4be..bee67e1ea0 100644 --- a/src/video/video_driver.cpp +++ b/src/video/video_driver.cpp @@ -56,6 +56,25 @@ void VideoDriver::GameThread() } } +/** + * Pause the game-loop for a bit, releasing the game-state lock. This allows, + * if the draw-tick requested this, the drawing to happen. + */ +void VideoDriver::GameLoopPause() +{ + /* If we are not called from the game-thread, ignore this request. */ + if (std::this_thread::get_id() != this->game_thread.get_id()) return; + + this->game_state_mutex.unlock(); + + { + /* See GameThread() for more details on this lock. */ + std::lock_guard lock(this->game_thread_wait_mutex); + } + + this->game_state_mutex.lock(); +} + /* static */ void VideoDriver::GameThreadThunk(VideoDriver *drv) { drv->GameThread(); diff --git a/src/video/video_driver.hpp b/src/video/video_driver.hpp index 8c2b9d4375..7a859565a6 100644 --- a/src/video/video_driver.hpp +++ b/src/video/video_driver.hpp @@ -179,6 +179,8 @@ public: this->change_blitter = new_blitter; } + void GameLoopPause(); + /** * Get the currently active instance of the video driver. */