diff --git a/src/video/sdl2_v.cpp b/src/video/sdl2_v.cpp --- a/src/video/sdl2_v.cpp +++ b/src/video/sdl2_v.cpp @@ -801,9 +801,9 @@ void VideoDriver_SDL::LoopOnce() /* The gameloop is the part that can run asynchronously. The rest * except sleeping can't. */ - if (_draw_mutex != nullptr) draw_lock.unlock(); + this->UnlockVideoBuffer(); GameLoop(); - if (_draw_mutex != nullptr) draw_lock.lock(); + this->LockVideoBuffer(); } /* Prevent drawing when switching mode, as windows can be removed when they should still appear. */ @@ -834,9 +834,9 @@ void VideoDriver_SDL::LoopOnce() auto now = std::chrono::steady_clock::now(); if (next_tick > now) { - if (_draw_mutex != nullptr) draw_lock.unlock(); + this->UnlockVideoBuffer(); std::this_thread::sleep_for(next_tick - now); - if (_draw_mutex != nullptr) draw_lock.lock(); + this->LockVideoBuffer(); } } #endif @@ -986,3 +986,14 @@ Dimension VideoDriver_SDL::GetScreenSize return { static_cast(mode.w), static_cast(mode.h) }; } + +bool VideoDriver_SDL::LockVideoBuffer() +{ + if (_draw_threaded) this->draw_lock.lock(); + return true; +} + +void VideoDriver_SDL::UnlockVideoBuffer() +{ + if (_draw_threaded) this->draw_lock.unlock(); +} diff --git a/src/video/sdl2_v.h b/src/video/sdl2_v.h --- a/src/video/sdl2_v.h +++ b/src/video/sdl2_v.h @@ -44,6 +44,8 @@ public: protected: Dimension GetScreenSize() const override; void InputLoop() override; + bool LockVideoBuffer() override; + void UnlockVideoBuffer() override; private: int PollEvent(); diff --git a/src/video/sdl_v.cpp b/src/video/sdl_v.cpp --- a/src/video/sdl_v.cpp +++ b/src/video/sdl_v.cpp @@ -706,7 +706,6 @@ void VideoDriver_SDL::MainLoop() CheckPaletteAnim(); std::thread draw_thread; - std::unique_lock draw_lock; if (_draw_threaded) { /* Initialise the mutex first, because that's the thing we *need* * directly in the newly created thread. */ @@ -714,7 +713,7 @@ void VideoDriver_SDL::MainLoop() if (_draw_mutex == nullptr) { _draw_threaded = false; } else { - draw_lock = std::unique_lock(*_draw_mutex); + this->draw_lock = std::unique_lock(*_draw_mutex); _draw_signal = new std::condition_variable_any(); _draw_continue = true; @@ -722,8 +721,8 @@ void VideoDriver_SDL::MainLoop() /* Free the mutex if we won't be able to use it. */ if (!_draw_threaded) { - draw_lock.unlock(); - draw_lock.release(); + this->draw_lock.unlock(); + this->draw_lock.release(); delete _draw_mutex; delete _draw_signal; _draw_mutex = nullptr; @@ -763,9 +762,9 @@ void VideoDriver_SDL::MainLoop() /* The gameloop is the part that can run asynchronously. The rest * except sleeping can't. */ - if (_draw_mutex != nullptr) draw_lock.unlock(); + this->UnlockVideoBuffer(); GameLoop(); - if (_draw_mutex != nullptr) draw_lock.lock(); + this->LockVideoBuffer(); } /* Prevent drawing when switching mode, as windows can be removed when they should still appear. */ @@ -794,9 +793,9 @@ void VideoDriver_SDL::MainLoop() auto now = std::chrono::steady_clock::now(); if (next_tick > now) { - if (_draw_mutex != nullptr) draw_lock.unlock(); + this->UnlockVideoBuffer(); std::this_thread::sleep_for(next_tick - now); - if (_draw_mutex != nullptr) draw_lock.lock(); + this->LockVideoBuffer(); } } } @@ -806,8 +805,8 @@ void VideoDriver_SDL::MainLoop() /* Sending signal if there is no thread blocked * is very valid and results in noop */ _draw_signal->notify_one(); - if (draw_lock.owns_lock()) draw_lock.unlock(); - draw_lock.release(); + if (this->draw_lock.owns_lock()) this->draw_lock.unlock(); + this->draw_lock.release(); draw_thread.join(); delete _draw_mutex; @@ -858,4 +857,15 @@ void VideoDriver_SDL::ReleaseBlitterLock if (_draw_mutex != nullptr) _draw_mutex->unlock(); } +bool VideoDriver_SDL::LockVideoBuffer() +{ + if (_draw_threaded) this->draw_lock.lock(); + return true; +} + +void VideoDriver_SDL::UnlockVideoBuffer() +{ + if (_draw_threaded) this->draw_lock.unlock(); +} + #endif /* WITH_SDL */ diff --git a/src/video/sdl_v.h b/src/video/sdl_v.h --- a/src/video/sdl_v.h +++ b/src/video/sdl_v.h @@ -39,8 +39,12 @@ public: protected: void InputLoop() override; + bool LockVideoBuffer() override; + void UnlockVideoBuffer() override; private: + std::unique_lock draw_lock; + int PollEvent(); bool CreateMainSurface(uint w, uint h); void SetupKeyboard(); diff --git a/src/video/video_driver.hpp b/src/video/video_driver.hpp --- a/src/video/video_driver.hpp +++ b/src/video/video_driver.hpp @@ -161,6 +161,19 @@ protected: */ virtual void InputLoop() {} + /** + * Make sure the video buffer is ready for drawing. + * @returns True if the video buffer has to be unlocked. + */ + virtual bool LockVideoBuffer() { + return false; + } + + /** + * Unlock a previously locked video buffer. + */ + virtual void UnlockVideoBuffer() {} + std::chrono::steady_clock::duration GetGameInterval() { return std::chrono::milliseconds(MILLISECONDS_PER_TICK); diff --git a/src/video/win32_v.cpp b/src/video/win32_v.cpp --- a/src/video/win32_v.cpp +++ b/src/video/win32_v.cpp @@ -1161,7 +1161,6 @@ void VideoDriver_Win32::MainLoop() auto next_draw_tick = cur_ticks; std::thread draw_thread; - std::unique_lock draw_lock; if (_draw_threaded) { /* Initialise the mutex first, because that's the thing we *need* @@ -1174,15 +1173,15 @@ void VideoDriver_Win32::MainLoop() } if (_draw_threaded) { - draw_lock = std::unique_lock(*_draw_mutex); + this->draw_lock = std::unique_lock(*_draw_mutex); _draw_continue = true; _draw_threaded = StartNewThread(&draw_thread, "ottd:draw-win32", &PaintWindowThread); /* Free the mutex if we won't be able to use it. */ if (!_draw_threaded) { - draw_lock.unlock(); - draw_lock.release(); + this->draw_lock.unlock(); + this->draw_lock.release(); delete _draw_mutex; delete _draw_signal; _draw_mutex = nullptr; @@ -1231,9 +1230,9 @@ void VideoDriver_Win32::MainLoop() /* The game loop is the part that can run asynchronously. * The rest except sleeping can't. */ - if (_draw_threaded) draw_lock.unlock(); + this->UnlockVideoBuffer(); GameLoop(); - if (_draw_threaded) draw_lock.lock(); + this->LockVideoBuffer(); } /* Prevent drawing when switching mode, as windows can be removed when they should still appear. */ @@ -1269,9 +1268,9 @@ void VideoDriver_Win32::MainLoop() /* Flush GDI buffer to ensure we don't conflict with the drawing thread. */ GdiFlush(); - if (_draw_mutex != nullptr) draw_lock.unlock(); + this->UnlockVideoBuffer(); std::this_thread::sleep_for(next_tick - now); - if (_draw_mutex != nullptr) draw_lock.lock(); + this->LockVideoBuffer(); } } } @@ -1281,8 +1280,8 @@ void VideoDriver_Win32::MainLoop() /* Sending signal if there is no thread blocked * is very valid and results in noop */ _draw_signal->notify_all(); - if (draw_lock.owns_lock()) draw_lock.unlock(); - draw_lock.release(); + if (this->draw_lock.owns_lock()) this->draw_lock.unlock(); + this->draw_lock.release(); draw_thread.join(); delete _draw_mutex; @@ -1383,3 +1382,14 @@ float VideoDriver_Win32::GetDPIScale() return cur_dpi > 0 ? cur_dpi / 96.0f : 1.0f; // Default Windows DPI value is 96. } + +bool VideoDriver_Win32::LockVideoBuffer() +{ + if (_draw_threaded) this->draw_lock.lock(); + return true; +} + +void VideoDriver_Win32::UnlockVideoBuffer() +{ + if (_draw_threaded) this->draw_lock.unlock(); +} diff --git a/src/video/win32_v.h b/src/video/win32_v.h --- a/src/video/win32_v.h +++ b/src/video/win32_v.h @@ -45,9 +45,15 @@ protected: Dimension GetScreenSize() const override; float GetDPIScale() override; void InputLoop() override; + bool LockVideoBuffer() override; + void UnlockVideoBuffer() override; private: + std::unique_lock draw_lock; + void CheckPaletteAnim(); + + static void PaintThreadThunk(VideoDriver_Win32 *drv); }; /** The factory for Windows' video driver. */