summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/video/allegro_v.cpp10
-rw-r--r--src/video/cocoa/cocoa_ogl.h2
-rw-r--r--src/video/cocoa/cocoa_ogl.mm7
-rw-r--r--src/video/cocoa/cocoa_v.mm6
-rw-r--r--src/video/dedicated_v.cpp2
-rw-r--r--src/video/null_v.cpp6
-rw-r--r--src/video/opengl.cpp23
-rw-r--r--src/video/opengl.h1
-rw-r--r--src/video/sdl2_default_v.cpp6
-rw-r--r--src/video/sdl2_opengl_v.cpp5
-rw-r--r--src/video/sdl2_opengl_v.h2
-rw-r--r--src/video/sdl2_v.cpp14
-rw-r--r--src/video/sdl_v.cpp10
-rw-r--r--src/video/video_driver.cpp106
-rw-r--r--src/video/video_driver.hpp32
-rw-r--r--src/video/win32_v.cpp19
-rw-r--r--src/video/win32_v.h2
17 files changed, 204 insertions, 49 deletions
diff --git a/src/video/allegro_v.cpp b/src/video/allegro_v.cpp
index 8ff1cc634..0c064873b 100644
--- a/src/video/allegro_v.cpp
+++ b/src/video/allegro_v.cpp
@@ -412,7 +412,7 @@ bool VideoDriver_Allegro::PollEvent()
*/
int _allegro_instance_count = 0;
-const char *VideoDriver_Allegro::Start(const StringList &parm)
+const char *VideoDriver_Allegro::Start(const StringList &param)
{
if (_allegro_instance_count == 0 && install_allegro(SYSTEM_AUTODETECT, &errno, nullptr)) {
DEBUG(driver, 0, "allegro: install_allegro failed '%s'", allegro_error);
@@ -440,6 +440,8 @@ const char *VideoDriver_Allegro::Start(const StringList &parm)
MarkWholeScreenDirty();
set_close_button_callback(HandleExitGameRequest);
+ this->is_game_threaded = !GetDriverParamBool(param, "no_threads") && !GetDriverParamBool(param, "no_thread");
+
return nullptr;
}
@@ -475,12 +477,16 @@ void VideoDriver_Allegro::InputLoop()
void VideoDriver_Allegro::MainLoop()
{
+ this->StartGameThread();
+
for (;;) {
- if (_exit_game) return;
+ if (_exit_game) break;
this->Tick();
this->SleepTillNextTick();
}
+
+ this->StopGameThread();
}
bool VideoDriver_Allegro::ChangeResolution(int w, int h)
diff --git a/src/video/cocoa/cocoa_ogl.h b/src/video/cocoa/cocoa_ogl.h
index 9d95cd606..fe8490932 100644
--- a/src/video/cocoa/cocoa_ogl.h
+++ b/src/video/cocoa/cocoa_ogl.h
@@ -33,6 +33,8 @@ public:
void ClearSystemSprites() override;
+ void PopulateSystemSprites() override;
+
bool HasAnimBuffer() override { return true; }
uint8 *GetAnimBuffer() override { return this->anim_buffer; }
diff --git a/src/video/cocoa/cocoa_ogl.mm b/src/video/cocoa/cocoa_ogl.mm
index c75de1e04..8d02428e0 100644
--- a/src/video/cocoa/cocoa_ogl.mm
+++ b/src/video/cocoa/cocoa_ogl.mm
@@ -214,6 +214,8 @@ const char *VideoDriver_CocoaOpenGL::Start(const StringList &param)
this->UpdateVideoModes();
MarkWholeScreenDirty();
+ this->is_game_threaded = !GetDriverParamBool(param, "no_threads") && !GetDriverParamBool(param, "no_thread");
+
return nullptr;
}
@@ -227,6 +229,11 @@ void VideoDriver_CocoaOpenGL::Stop()
CGLReleaseContext(this->gl_context);
}
+void VideoDriver_CocoaOpenGL::PopulateSystemSprites()
+{
+ OpenGLBackend::Get()->PopulateCursorCache();
+}
+
void VideoDriver_CocoaOpenGL::ClearSystemSprites()
{
CGLSetCurrentContext(this->gl_context);
diff --git a/src/video/cocoa/cocoa_v.mm b/src/video/cocoa/cocoa_v.mm
index 56d559f0d..8d1fd4447 100644
--- a/src/video/cocoa/cocoa_v.mm
+++ b/src/video/cocoa/cocoa_v.mm
@@ -435,6 +435,8 @@ void VideoDriver_Cocoa::InputLoop()
/** Main game loop. */
void VideoDriver_Cocoa::MainLoopReal()
{
+ this->StartGameThread();
+
for (;;) {
@autoreleasepool {
if (_exit_game) {
@@ -447,6 +449,8 @@ void VideoDriver_Cocoa::MainLoopReal()
this->SleepTillNextTick();
}
}
+
+ this->StopGameThread();
}
@@ -558,6 +562,8 @@ const char *VideoDriver_CocoaQuartz::Start(const StringList &param)
this->GameSizeChanged();
this->UpdateVideoModes();
+ this->is_game_threaded = !GetDriverParamBool(param, "no_threads") && !GetDriverParamBool(param, "no_thread");
+
return nullptr;
}
diff --git a/src/video/dedicated_v.cpp b/src/video/dedicated_v.cpp
index 22b9dbb1a..ea1c14cff 100644
--- a/src/video/dedicated_v.cpp
+++ b/src/video/dedicated_v.cpp
@@ -267,6 +267,8 @@ void VideoDriver_Dedicated::MainLoop()
}
}
+ this->is_game_threaded = false;
+
/* Done loading, start game! */
if (!_networking) {
diff --git a/src/video/null_v.cpp b/src/video/null_v.cpp
index 35dd7b075..4f7843b75 100644
--- a/src/video/null_v.cpp
+++ b/src/video/null_v.cpp
@@ -48,9 +48,9 @@ void VideoDriver_Null::MainLoop()
uint i;
for (i = 0; i < this->ticks; i++) {
- GameLoop();
- InputLoop();
- UpdateWindows();
+ ::GameLoop();
+ ::InputLoop();
+ ::UpdateWindows();
}
}
diff --git a/src/video/opengl.cpp b/src/video/opengl.cpp
index 60f2a1bcd..64ea02cf2 100644
--- a/src/video/opengl.cpp
+++ b/src/video/opengl.cpp
@@ -1036,7 +1036,23 @@ void OpenGLBackend::DrawMouseCursor()
_cur_dpi = &_screen;
for (uint i = 0; i < _cursor.sprite_count; ++i) {
SpriteID sprite = _cursor.sprite_seq[i].sprite;
- const Sprite *spr = GetSprite(sprite, ST_NORMAL);
+
+ /* Sprites are cached by PopulateCursorCache(). */
+ if (this->cursor_cache.Contains(sprite)) {
+ const Sprite *spr = GetSprite(sprite, ST_NORMAL);
+
+ this->RenderOglSprite((OpenGLSprite *)this->cursor_cache.Get(sprite)->data, _cursor.sprite_seq[i].pal,
+ _cursor.pos.x + _cursor.sprite_pos[i].x + UnScaleByZoom(spr->x_offs, ZOOM_LVL_GUI),
+ _cursor.pos.y + _cursor.sprite_pos[i].y + UnScaleByZoom(spr->y_offs, ZOOM_LVL_GUI),
+ ZOOM_LVL_GUI);
+ }
+ }
+}
+
+void OpenGLBackend::PopulateCursorCache()
+{
+ for (uint i = 0; i < _cursor.sprite_count; ++i) {
+ SpriteID sprite = _cursor.sprite_seq[i].sprite;
if (!this->cursor_cache.Contains(sprite)) {
Sprite *old = this->cursor_cache.Insert(sprite, (Sprite *)GetRawSprite(sprite, ST_NORMAL, &SimpleSpriteAlloc, this));
@@ -1046,11 +1062,6 @@ void OpenGLBackend::DrawMouseCursor()
free(old);
}
}
-
- this->RenderOglSprite((OpenGLSprite *)this->cursor_cache.Get(sprite)->data, _cursor.sprite_seq[i].pal,
- _cursor.pos.x + _cursor.sprite_pos[i].x + UnScaleByZoom(spr->x_offs, ZOOM_LVL_GUI),
- _cursor.pos.y + _cursor.sprite_pos[i].y + UnScaleByZoom(spr->y_offs, ZOOM_LVL_GUI),
- ZOOM_LVL_GUI);
}
}
diff --git a/src/video/opengl.h b/src/video/opengl.h
index b66d585e5..3919179e4 100644
--- a/src/video/opengl.h
+++ b/src/video/opengl.h
@@ -88,6 +88,7 @@ public:
void Paint();
void DrawMouseCursor();
+ void PopulateCursorCache();
void ClearCursorCache();
void *GetVideoBuffer();
diff --git a/src/video/sdl2_default_v.cpp b/src/video/sdl2_default_v.cpp
index e61405ff8..4db1c33ce 100644
--- a/src/video/sdl2_default_v.cpp
+++ b/src/video/sdl2_default_v.cpp
@@ -107,13 +107,7 @@ void VideoDriver_SDL_Default::Paint()
break;
case Blitter::PALETTE_ANIMATION_BLITTER: {
- bool need_buf = _screen.dst_ptr == nullptr;
- if (need_buf) _screen.dst_ptr = this->GetVideoPointer();
blitter->PaletteAnimate(this->local_palette);
- if (need_buf) {
- this->ReleaseVideoPointer();
- _screen.dst_ptr = nullptr;
- }
break;
}
diff --git a/src/video/sdl2_opengl_v.cpp b/src/video/sdl2_opengl_v.cpp
index 4bd66a994..13ee082ba 100644
--- a/src/video/sdl2_opengl_v.cpp
+++ b/src/video/sdl2_opengl_v.cpp
@@ -110,6 +110,11 @@ const char *VideoDriver_SDL_OpenGL::AllocateContext()
return OpenGLBackend::Create(&GetOGLProcAddressCallback);
}
+void VideoDriver_SDL_OpenGL::PopulateSystemSprites()
+{
+ OpenGLBackend::Get()->PopulateCursorCache();
+}
+
void VideoDriver_SDL_OpenGL::ClearSystemSprites()
{
OpenGLBackend::Get()->ClearCursorCache();
diff --git a/src/video/sdl2_opengl_v.h b/src/video/sdl2_opengl_v.h
index 55697b43b..f749b1f45 100644
--- a/src/video/sdl2_opengl_v.h
+++ b/src/video/sdl2_opengl_v.h
@@ -24,6 +24,8 @@ public:
void ClearSystemSprites() override;
+ void PopulateSystemSprites() override;
+
bool HasAnimBuffer() override { return true; }
uint8 *GetAnimBuffer() override { return this->anim_buffer; }
diff --git a/src/video/sdl2_v.cpp b/src/video/sdl2_v.cpp
index c1925c7da..62675280c 100644
--- a/src/video/sdl2_v.cpp
+++ b/src/video/sdl2_v.cpp
@@ -541,14 +541,14 @@ const char *VideoDriver_SDL_Base::Initialize()
return nullptr;
}
-const char *VideoDriver_SDL_Base::Start(const StringList &parm)
+const char *VideoDriver_SDL_Base::Start(const StringList &param)
{
if (BlitterFactory::GetCurrentBlitter()->GetScreenDepth() == 0) return "Only real blitters supported";
const char *error = this->Initialize();
if (error != nullptr) return error;
- this->startup_display = FindStartupDisplay(GetDriverParamInt(parm, "display", -1));
+ this->startup_display = FindStartupDisplay(GetDriverParamInt(param, "display", -1));
if (!CreateMainSurface(_cur_resolution.width, _cur_resolution.height, false)) {
return SDL_GetError();
@@ -562,6 +562,12 @@ const char *VideoDriver_SDL_Base::Start(const StringList &parm)
SDL_StopTextInput();
this->edit_box_focused = false;
+#ifdef __EMSCRIPTEN__
+ this->is_game_threaded = false;
+#else
+ this->is_game_threaded = !GetDriverParamBool(param, "no_threads") && !GetDriverParamBool(param, "no_thread");
+#endif
+
return nullptr;
}
@@ -637,9 +643,13 @@ void VideoDriver_SDL_Base::MainLoop()
/* Run the main loop event-driven, based on RequestAnimationFrame. */
emscripten_set_main_loop_arg(&this->EmscriptenLoop, this, 0, 1);
#else
+ this->StartGameThread();
+
while (!_exit_game) {
LoopOnce();
}
+
+ this->StopGameThread();
#endif
}
diff --git a/src/video/sdl_v.cpp b/src/video/sdl_v.cpp
index 2ecb9510c..03f174794 100644
--- a/src/video/sdl_v.cpp
+++ b/src/video/sdl_v.cpp
@@ -570,10 +570,10 @@ bool VideoDriver_SDL::PollEvent()
return true;
}
-const char *VideoDriver_SDL::Start(const StringList &parm)
+const char *VideoDriver_SDL::Start(const StringList &param)
{
char buf[30];
- _use_hwpalette = GetDriverParamInt(parm, "hw_palette", 2);
+ _use_hwpalette = GetDriverParamInt(param, "hw_palette", 2);
/* Just on the offchance the audio subsystem started before the video system,
* check whether any part of SDL has been initialised before getting here.
@@ -599,6 +599,8 @@ const char *VideoDriver_SDL::Start(const StringList &parm)
MarkWholeScreenDirty();
SetupKeyboard();
+ this->is_game_threaded = !GetDriverParamBool(param, "no_threads") && !GetDriverParamBool(param, "no_thread");
+
return nullptr;
}
@@ -647,12 +649,16 @@ void VideoDriver_SDL::InputLoop()
void VideoDriver_SDL::MainLoop()
{
+ this->StartGameThread();
+
for (;;) {
if (_exit_game) break;
this->Tick();
this->SleepTillNextTick();
}
+
+ this->StopGameThread();
}
bool VideoDriver_SDL::ChangeResolution(int w, int h)
diff --git a/src/video/video_driver.cpp b/src/video/video_driver.cpp
index 49337c03a..e313329d5 100644
--- a/src/video/video_driver.cpp
+++ b/src/video/video_driver.cpp
@@ -8,9 +8,9 @@
/** @file video_driver.cpp Common code between video driver implementations. */
#include "../stdafx.h"
-#include "../debug.h"
#include "../core/random_func.hpp"
#include "../network/network.h"
+#include "../debug.h"
#include "../gfx_func.h"
#include "../progress.h"
#include "../thread.h"
@@ -19,40 +19,84 @@
bool _video_hw_accel; ///< Whether to consider hardware accelerated video drivers.
-void VideoDriver::Tick()
+void VideoDriver::GameLoop()
{
- auto cur_ticks = std::chrono::steady_clock::now();
+ this->next_game_tick += this->GetGameInterval();
- if (cur_ticks >= this->next_game_tick) {
- this->next_game_tick += this->GetGameInterval();
- /* Avoid next_game_tick getting behind more and more if it cannot keep up. */
- if (this->next_game_tick < cur_ticks - ALLOWED_DRIFT * this->GetGameInterval()) this->next_game_tick = cur_ticks;
+ /* Avoid next_game_tick getting behind more and more if it cannot keep up. */
+ auto now = std::chrono::steady_clock::now();
+ if (this->next_game_tick < now - ALLOWED_DRIFT * this->GetGameInterval()) this->next_game_tick = now;
+
+ {
+ std::lock_guard<std::mutex> lock(this->game_state_mutex);
- /* The game loop is the part that can run asynchronously.
- * The rest except sleeping can't. */
- this->UnlockVideoBuffer();
::GameLoop();
- this->LockVideoBuffer();
+ }
+}
+
+void VideoDriver::GameThread()
+{
+ while (!_exit_game) {
+ this->GameLoop();
+
+ auto now = std::chrono::steady_clock::now();
+ if (this->next_game_tick > now) {
+ std::this_thread::sleep_for(this->next_game_tick - now);
+ } else {
+ /* Ensure we yield this thread if drawings wants to take a lock on
+ * the game state. This is mainly because most OSes have an
+ * optimization that if you unlock/lock a mutex in the same thread
+ * quickly, it will never context switch even if there is another
+ * thread waiting to take the lock on the same mutex. */
+ std::lock_guard<std::mutex> lock(this->game_thread_wait_mutex);
+ }
+ }
+}
+
+/* static */ void VideoDriver::GameThreadThunk(VideoDriver *drv)
+{
+ drv->GameThread();
+}
+
+void VideoDriver::StartGameThread()
+{
+ if (this->is_game_threaded) {
+ this->is_game_threaded = StartNewThread(&this->game_thread, "ottd:game", &VideoDriver::GameThreadThunk, this);
+ }
+
+ DEBUG(driver, 1, "using %sthread for game-loop", this->is_game_threaded ? "" : "no ");
+}
+
+void VideoDriver::StopGameThread()
+{
+ if (!this->is_game_threaded) return;
+
+ this->game_thread.join();
+}
+
+void VideoDriver::Tick()
+{
+ if (!this->is_game_threaded && std::chrono::steady_clock::now() >= this->next_game_tick) {
+ this->GameLoop();
/* For things like dedicated server, don't run a separate draw-tick. */
if (!this->HasGUI()) {
::InputLoop();
- UpdateWindows();
+ ::UpdateWindows();
this->next_draw_tick = this->next_game_tick;
}
}
- /* Prevent drawing when switching mode, as windows can be removed when they should still appear. */
- if (this->HasGUI() && cur_ticks >= this->next_draw_tick && (_switch_mode == SM_NONE || _game_mode == GM_BOOTSTRAP || HasModalProgress())) {
+ auto now = std::chrono::steady_clock::now();
+ if (this->HasGUI() && now >= this->next_draw_tick) {
this->next_draw_tick += this->GetDrawInterval();
/* Avoid next_draw_tick getting behind more and more if it cannot keep up. */
- if (this->next_draw_tick < cur_ticks - ALLOWED_DRIFT * this->GetDrawInterval()) this->next_draw_tick = cur_ticks;
+ if (this->next_draw_tick < now - ALLOWED_DRIFT * this->GetDrawInterval()) this->next_draw_tick = now;
/* Keep the interactive randomizer a bit more random by requesting
* new values when-ever we can. */
InteractiveRandom();
- while (this->PollEvent()) {}
this->InputLoop();
/* Check if the fast-forward button is still pressed. */
@@ -64,23 +108,41 @@ void VideoDriver::Tick()
this->fast_forward_via_key = false;
}
- ::InputLoop();
- UpdateWindows();
+ {
+ /* Tell the game-thread to stop so we can have a go. */
+ std::lock_guard<std::mutex> lock_wait(this->game_thread_wait_mutex);
+ std::lock_guard<std::mutex> lock_state(this->game_state_mutex);
+
+ this->LockVideoBuffer();
+
+ while (this->PollEvent()) {}
+ ::InputLoop();
+
+ /* Prevent drawing when switching mode, as windows can be removed when they should still appear. */
+ if (_game_mode == GM_BOOTSTRAP || _switch_mode == SM_NONE || HasModalProgress()) {
+ ::UpdateWindows();
+ }
+
+ this->PopulateSystemSprites();
+ }
this->CheckPaletteAnim();
this->Paint();
+
+ this->UnlockVideoBuffer();
}
}
void VideoDriver::SleepTillNextTick()
{
- /* See how much time there is till we have to process the next event, and try to hit that as close as possible. */
- auto next_tick = std::min(this->next_draw_tick, this->next_game_tick);
+ auto next_tick = this->next_draw_tick;
auto now = std::chrono::steady_clock::now();
+ if (!this->is_game_threaded) {
+ next_tick = min(next_tick, this->next_game_tick);
+ }
+
if (next_tick > now) {
- this->UnlockVideoBuffer();
std::this_thread::sleep_for(next_tick - now);
- this->LockVideoBuffer();
}
}
diff --git a/src/video/video_driver.hpp b/src/video/video_driver.hpp
index cfb853dba..423e46436 100644
--- a/src/video/video_driver.hpp
+++ b/src/video/video_driver.hpp
@@ -16,7 +16,11 @@
#include "../gfx_func.h"
#include "../settings_type.h"
#include "../zoom_type.h"
+#include <atomic>
#include <chrono>
+#include <condition_variable>
+#include <mutex>
+#include <thread>
#include <vector>
extern std::string _ini_videodriver;
@@ -31,6 +35,8 @@ class VideoDriver : public Driver {
const uint DEFAULT_WINDOW_HEIGHT = 480u; ///< Default window height.
public:
+ VideoDriver() : is_game_threaded(true) {}
+
/**
* Mark a particular area dirty.
* @param left The left most line of the dirty area.
@@ -84,6 +90,11 @@ public:
}
/**
+ * Populate all sprites in cache.
+ */
+ virtual void PopulateSystemSprites() {}
+
+ /**
* Clear all cached sprites.
*/
virtual void ClearSystemSprites() {}
@@ -240,6 +251,16 @@ protected:
virtual bool PollEvent() { return false; };
/**
+ * Start the loop for game-tick.
+ */
+ void StartGameThread();
+
+ /**
+ * Stop the loop for the game-tick. This can still tick at most one time before truly shutting down.
+ */
+ void StopGameThread();
+
+ /**
* Give the video-driver a tick.
* It will process any potential game-tick and/or draw-tick, and/or any
* other video-driver related event.
@@ -271,6 +292,17 @@ protected:
bool fast_forward_key_pressed; ///< The fast-forward key is being pressed.
bool fast_forward_via_key; ///< The fast-forward was enabled by key press.
+
+ bool is_game_threaded;
+ std::thread game_thread;
+ std::mutex game_state_mutex;
+ std::mutex game_thread_wait_mutex;
+
+ static void GameThreadThunk(VideoDriver *drv);
+
+private:
+ void GameLoop();
+ void GameThread();
};
#endif /* VIDEO_VIDEO_DRIVER_HPP */
diff --git a/src/video/win32_v.cpp b/src/video/win32_v.cpp
index 894d1e05d..8030380ce 100644
--- a/src/video/win32_v.cpp
+++ b/src/video/win32_v.cpp
@@ -864,12 +864,16 @@ bool VideoDriver_Win32Base::PollEvent()
void VideoDriver_Win32Base::MainLoop()
{
+ this->StartGameThread();
+
for (;;) {
if (_exit_game) break;
this->Tick();
this->SleepTillNextTick();
}
+
+ this->StopGameThread();
}
void VideoDriver_Win32Base::ClientSizeChanged(int w, int h, bool force)
@@ -995,6 +999,8 @@ const char *VideoDriver_Win32GDI::Start(const StringList &param)
MarkWholeScreenDirty();
+ this->is_game_threaded = !GetDriverParamBool(param, "no_threads") && !GetDriverParamBool(param, "no_thread");
+
return nullptr;
}
@@ -1115,13 +1121,7 @@ void VideoDriver_Win32GDI::Paint()
break;
case Blitter::PALETTE_ANIMATION_BLITTER: {
- bool need_buf = _screen.dst_ptr == nullptr;
- if (need_buf) _screen.dst_ptr = this->GetVideoPointer();
blitter->PaletteAnimate(_local_palette);
- if (need_buf) {
- this->ReleaseVideoPointer();
- _screen.dst_ptr = nullptr;
- }
break;
}
@@ -1291,6 +1291,8 @@ const char *VideoDriver_Win32OpenGL::Start(const StringList &param)
MarkWholeScreenDirty();
+ this->is_game_threaded = !GetDriverParamBool(param, "no_threads") && !GetDriverParamBool(param, "no_thread");
+
return nullptr;
}
@@ -1371,6 +1373,11 @@ bool VideoDriver_Win32OpenGL::AfterBlitterChange()
return true;
}
+void VideoDriver_Win32OpenGL::PopulateSystemSprites()
+{
+ OpenGLBackend::Get()->PopulateCursorCache();
+}
+
void VideoDriver_Win32OpenGL::ClearSystemSprites()
{
OpenGLBackend::Get()->ClearCursorCache();
diff --git a/src/video/win32_v.h b/src/video/win32_v.h
index fc705c3f1..93ebdd83e 100644
--- a/src/video/win32_v.h
+++ b/src/video/win32_v.h
@@ -129,6 +129,8 @@ public:
bool UseSystemCursor() override { return true; }
+ void PopulateSystemSprites() override;
+
void ClearSystemSprites() override;
bool HasAnimBuffer() override { return true; }