summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Lutz <michi@icosahedron.de>2021-05-01 19:39:03 +0200
committerMichael Lutz <michi@icosahedron.de>2021-05-02 17:57:24 +0200
commit91b8ce073f54dff48cd61186c32d0a720a2abb4d (patch)
treefc9c162bc66bb3d374a3f2543483163350e03d51
parent2cf5df2a50e9f28ef4ba93a765bfcf79afcbb20c (diff)
downloadopenttd-91b8ce073f54dff48cd61186c32d0a720a2abb4d.tar.xz
Codechange: Generalise the delayed blitter change to a generic video driver command queue.
-rw-r--r--src/gfxinit.cpp24
-rw-r--r--src/video/video_driver.cpp26
-rw-r--r--src/video/video_driver.hpp39
3 files changed, 55 insertions, 34 deletions
diff --git a/src/gfxinit.cpp b/src/gfxinit.cpp
index b744eaa5e..c0ba4c4d4 100644
--- a/src/gfxinit.cpp
+++ b/src/gfxinit.cpp
@@ -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.
* @return False when nothing changed, true otherwise.
@@ -309,7 +331,7 @@ static bool SwitchNewGRFBlitter()
if (BlitterFactory::GetBlitterFactory(repl_blitter) == nullptr) continue;
/* 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;
}
diff --git a/src/video/video_driver.cpp b/src/video/video_driver.cpp
index d8fbe0400..2f8efc4ad 100644
--- a/src/video/video_driver.cpp
+++ b/src/video/video_driver.cpp
@@ -97,27 +97,6 @@ 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(false);
-}
-
void VideoDriver::Tick()
{
if (!this->is_game_threaded && std::chrono::steady_clock::now() >= this->next_game_tick) {
@@ -159,10 +138,7 @@ void VideoDriver::Tick()
this->LockVideoBuffer();
- if (this->change_blitter != nullptr) {
- this->RealChangeBlitter(this->change_blitter);
- this->change_blitter = nullptr;
- }
+ this->DrainCommandQueue();
while (this->PollEvent()) {}
::InputLoop();
diff --git a/src/video/video_driver.hpp b/src/video/video_driver.hpp
index 4964e01cb..db522a761 100644
--- a/src/video/video_driver.hpp
+++ b/src/video/video_driver.hpp
@@ -22,6 +22,7 @@
#include <mutex>
#include <thread>
#include <vector>
+#include <functional>
extern std::string _ini_videodriver;
extern std::vector<Dimension> _resolutions;
@@ -36,7 +37,7 @@ class VideoDriver : public Driver {
const uint DEFAULT_WINDOW_HEIGHT = 480u; ///< Default window height.
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.
@@ -178,12 +179,16 @@ public:
}
/**
- * Queue a request to change the blitter. This is not executed immediately,
- * but instead on the next draw-tick.
+ * Queue a function to be called on the main thread with game state
+ * 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();
@@ -328,11 +333,29 @@ protected:
static void GameThreadThunk(VideoDriver *drv);
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 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 */