1
0
Fork 0

Codechange: Generalise the delayed blitter change to a generic video driver command queue.

pull/9140/head
Michael Lutz 2021-05-01 19:39:03 +02:00
parent 2cf5df2a50
commit 91b8ce073f
3 changed files with 55 additions and 34 deletions

View File

@ -244,6 +244,28 @@ static void LoadSpriteTables()
} }
static void RealChangeBlitter(const char *repl_blitter)
{
const char *cur_blitter = BlitterFactory::GetCurrentBlitter()->GetName();
if (strcmp(cur_blitter, repl_blitter) == 0) return;
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 (!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");
}
/* Clear caches that might have sprites for another blitter. */
VideoDriver::GetInstance()->ClearSystemSprites();
ClearFontCache();
GfxClearSpriteCache();
ReInitAllWindows(false);
}
/** /**
* Check blitter needed by NewGRF config and switch if needed. * Check blitter needed by NewGRF config and switch if needed.
* @return False when nothing changed, true otherwise. * @return False when nothing changed, true otherwise.
@ -309,7 +331,7 @@ static bool SwitchNewGRFBlitter()
if (BlitterFactory::GetBlitterFactory(repl_blitter) == nullptr) continue; if (BlitterFactory::GetBlitterFactory(repl_blitter) == nullptr) continue;
/* Inform the video driver we want to switch blitter as soon as possible. */ /* Inform the video driver we want to switch blitter as soon as possible. */
VideoDriver::GetInstance()->ChangeBlitter(repl_blitter); VideoDriver::GetInstance()->QueueOnMainThread(std::bind(&RealChangeBlitter, repl_blitter));
break; break;
} }

View File

@ -97,27 +97,6 @@ void VideoDriver::StopGameThread()
this->game_thread.join(); 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(false);
}
void VideoDriver::Tick() void VideoDriver::Tick()
{ {
if (!this->is_game_threaded && std::chrono::steady_clock::now() >= this->next_game_tick) { if (!this->is_game_threaded && std::chrono::steady_clock::now() >= this->next_game_tick) {
@ -159,10 +138,7 @@ void VideoDriver::Tick()
this->LockVideoBuffer(); this->LockVideoBuffer();
if (this->change_blitter != nullptr) { this->DrainCommandQueue();
this->RealChangeBlitter(this->change_blitter);
this->change_blitter = nullptr;
}
while (this->PollEvent()) {} while (this->PollEvent()) {}
::InputLoop(); ::InputLoop();

View File

@ -22,6 +22,7 @@
#include <mutex> #include <mutex>
#include <thread> #include <thread>
#include <vector> #include <vector>
#include <functional>
extern std::string _ini_videodriver; extern std::string _ini_videodriver;
extern std::vector<Dimension> _resolutions; extern std::vector<Dimension> _resolutions;
@ -36,7 +37,7 @@ class VideoDriver : public Driver {
const uint DEFAULT_WINDOW_HEIGHT = 480u; ///< Default window height. const uint DEFAULT_WINDOW_HEIGHT = 480u; ///< Default window height.
public: public:
VideoDriver() : fast_forward_key_pressed(false), fast_forward_via_key(false), is_game_threaded(true), change_blitter(nullptr) {} VideoDriver() : fast_forward_key_pressed(false), fast_forward_via_key(false), is_game_threaded(true) {}
/** /**
* Mark a particular area dirty. * Mark a particular area dirty.
@ -178,12 +179,16 @@ public:
} }
/** /**
* Queue a request to change the blitter. This is not executed immediately, * Queue a function to be called on the main thread with game state
* but instead on the next draw-tick. * lock held and video buffer locked. Queued functions will be
* executed on the next draw tick.
* @param func Function to call.
*/ */
void ChangeBlitter(const char *new_blitter) void QueueOnMainThread(std::function<void()> &&func)
{ {
this->change_blitter = new_blitter; std::lock_guard<std::mutex> lock(this->cmd_queue_mutex);
this->cmd_queue.emplace_back(std::forward<std::function<void()>>(func));
} }
void GameLoopPause(); void GameLoopPause();
@ -328,11 +333,29 @@ protected:
static void GameThreadThunk(VideoDriver *drv); static void GameThreadThunk(VideoDriver *drv);
private: private:
std::mutex cmd_queue_mutex;
std::vector<std::function<void()>> cmd_queue;
/** Execute all queued commands. */
void DrainCommandQueue()
{
std::vector<std::function<void()>> cmds{};
{
/* Exchange queue with an empty one to limit the time we
* hold the mutex. This also ensures that queued functions can
* add new functions to the queue without everything blocking. */
std::lock_guard<std::mutex> lock(this->cmd_queue_mutex);
cmds.swap(this->cmd_queue);
}
for (auto &f : cmds) {
f();
}
}
void GameLoop(); void GameLoop();
void GameThread(); 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 */ #endif /* VIDEO_VIDEO_DRIVER_HPP */