From 8946b41d20d6be59da23b98add273955b43450fd Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Mon, 8 Mar 2021 14:50:06 +0100 Subject: [PATCH] Fix: ensure switching blitter happens in the main thread This because video-drivers might need to make changes to their context, which for most video-drivers has to be done in the same thread as the window was created; main thread in our case. --- src/gfxinit.cpp | 11 ++--------- src/video/video_driver.cpp | 29 +++++++++++++++++++++++++++++ src/video/video_driver.hpp | 14 +++++++++++++- 3 files changed, 44 insertions(+), 10 deletions(-) diff --git a/src/gfxinit.cpp b/src/gfxinit.cpp index 2883d3ee3b..7e2c2cc625 100644 --- a/src/gfxinit.cpp +++ b/src/gfxinit.cpp @@ -308,18 +308,11 @@ static bool SwitchNewGRFBlitter() } if (BlitterFactory::GetBlitterFactory(repl_blitter) == nullptr) continue; - DEBUG(misc, 1, "Switching blitter from '%s' to '%s'... ", cur_blitter, repl_blitter); - Blitter *new_blitter = BlitterFactory::SelectBlitter(repl_blitter); - if (new_blitter == nullptr) NOT_REACHED(); - DEBUG(misc, 1, "Successfully switched to %s.", repl_blitter); + /* Inform the video driver we want to switch blitter as soon as possible. */ + VideoDriver::GetInstance()->ChangeBlitter(repl_blitter); break; } - if (!VideoDriver::GetInstance()->AfterBlitterChange()) { - /* Failed to switch blitter, let's hope we can return to the old one. */ - if (BlitterFactory::SelectBlitter(cur_blitter) == nullptr || !VideoDriver::GetInstance()->AfterBlitterChange()) usererror("Failed to reinitialize video driver. Specify a fixed blitter in the config"); - } - return true; } diff --git a/src/video/video_driver.cpp b/src/video/video_driver.cpp index e313329d59..1b7fc6a4be 100644 --- a/src/video/video_driver.cpp +++ b/src/video/video_driver.cpp @@ -10,8 +10,11 @@ #include "../stdafx.h" #include "../core/random_func.hpp" #include "../network/network.h" +#include "../blitter/factory.hpp" #include "../debug.h" +#include "../fontcache.h" #include "../gfx_func.h" +#include "../gfxinit.h" #include "../progress.h" #include "../thread.h" #include "../window_func.h" @@ -74,6 +77,27 @@ void VideoDriver::StopGameThread() this->game_thread.join(); } +void VideoDriver::RealChangeBlitter(const char *repl_blitter) +{ + const char *cur_blitter = BlitterFactory::GetCurrentBlitter()->GetName(); + + DEBUG(driver, 1, "Switching blitter from '%s' to '%s'... ", cur_blitter, repl_blitter); + Blitter *new_blitter = BlitterFactory::SelectBlitter(repl_blitter); + if (new_blitter == nullptr) NOT_REACHED(); + DEBUG(driver, 1, "Successfully switched to %s.", repl_blitter); + + if (!this->AfterBlitterChange()) { + /* Failed to switch blitter, let's hope we can return to the old one. */ + if (BlitterFactory::SelectBlitter(cur_blitter) == nullptr || !this->AfterBlitterChange()) usererror("Failed to reinitialize video driver. Specify a fixed blitter in the config"); + } + + /* Clear caches that might have sprites for another blitter. */ + this->ClearSystemSprites(); + ClearFontCache(); + GfxClearSpriteCache(); + ReInitAllWindows(); +} + void VideoDriver::Tick() { if (!this->is_game_threaded && std::chrono::steady_clock::now() >= this->next_game_tick) { @@ -115,6 +139,11 @@ void VideoDriver::Tick() this->LockVideoBuffer(); + if (this->change_blitter != nullptr) { + this->RealChangeBlitter(this->change_blitter); + this->change_blitter = nullptr; + } + while (this->PollEvent()) {} ::InputLoop(); diff --git a/src/video/video_driver.hpp b/src/video/video_driver.hpp index 423e46436f..4b2de253a3 100644 --- a/src/video/video_driver.hpp +++ b/src/video/video_driver.hpp @@ -35,7 +35,7 @@ class VideoDriver : public Driver { const uint DEFAULT_WINDOW_HEIGHT = 480u; ///< Default window height. public: - VideoDriver() : is_game_threaded(true) {} + VideoDriver() : is_game_threaded(true), change_blitter(nullptr) {} /** * Mark a particular area dirty. @@ -161,6 +161,15 @@ public: return ZOOM_LVL_OUT_4X; } + /** + * Queue a request to change the blitter. This is not executed immediately, + * but instead on the next draw-tick. + */ + void ChangeBlitter(const char *new_blitter) + { + this->change_blitter = new_blitter; + } + /** * Get the currently active instance of the video driver. */ @@ -303,6 +312,9 @@ protected: private: void GameLoop(); void GameThread(); + void RealChangeBlitter(const char *repl_blitter); + + const char *change_blitter; ///< Request to change the blitter. nullptr if no pending request. }; #endif /* VIDEO_VIDEO_DRIVER_HPP */