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.
This commit is contained in:
Patric Stout 2021-03-08 14:50:06 +01:00 committed by Patric Stout
parent e56d2c63c3
commit 8946b41d20
3 changed files with 44 additions and 10 deletions

View File

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

View File

@ -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();

View File

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