Changeset - r24850:df6b081a960b
[Not reviewed]
master
0 7 0
Patric Stout - 3 years ago 2021-02-20 09:49:27
truebrain@openttd.org
Codechange: use (Un)LockVideoBuffer() to manage video buffer
7 files changed with 80 insertions and 24 deletions:
0 comments (0 inline, 0 general)
src/video/sdl2_v.cpp
Show inline comments
 
@@ -798,15 +798,15 @@ void VideoDriver_SDL::LoopOnce()
 
			/* Avoid next_game_tick getting behind more and more if it cannot keep up. */
 
			if (next_game_tick < cur_ticks - ALLOWED_DRIFT * this->GetGameInterval()) next_game_tick = cur_ticks;
 
		}
 

	
 
		/* 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. */
 
	if (cur_ticks >= next_draw_tick && (_switch_mode == SM_NONE || HasModalProgress())) {
 
		next_draw_tick += this->GetDrawInterval();
 
		/* Avoid next_draw_tick getting behind more and more if it cannot keep up. */
 
@@ -831,15 +831,15 @@ void VideoDriver_SDL::LoopOnce()
 
	if (!_fast_forward || _pause_mode) {
 
		/* 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(next_draw_tick, next_game_tick);
 
		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
 
}
 

	
 
void VideoDriver_SDL::MainLoop()
 
@@ -983,6 +983,17 @@ Dimension VideoDriver_SDL::GetScreenSize
 
{
 
	SDL_DisplayMode mode;
 
	if (SDL_GetCurrentDisplayMode(this->startup_display, &mode) != 0) return VideoDriver::GetScreenSize();
 

	
 
	return { static_cast<uint>(mode.w), static_cast<uint>(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();
 
}
src/video/sdl2_v.h
Show inline comments
 
@@ -41,12 +41,14 @@ public:
 

	
 
	const char *GetName() const override { return "sdl"; }
 

	
 
protected:
 
	Dimension GetScreenSize() const override;
 
	void InputLoop() override;
 
	bool LockVideoBuffer() override;
 
	void UnlockVideoBuffer() override;
 

	
 
private:
 
	int PollEvent();
 
	void LoopOnce();
 
	void MainLoopCleanup();
 
	bool CreateMainSurface(uint w, uint h, bool resize);
src/video/sdl_v.cpp
Show inline comments
 
@@ -703,30 +703,29 @@ void VideoDriver_SDL::MainLoop()
 
	auto next_game_tick = cur_ticks;
 
	auto next_draw_tick = cur_ticks;
 

	
 
	CheckPaletteAnim();
 

	
 
	std::thread draw_thread;
 
	std::unique_lock<std::recursive_mutex> draw_lock;
 
	if (_draw_threaded) {
 
		/* Initialise the mutex first, because that's the thing we *need*
 
		 * directly in the newly created thread. */
 
		_draw_mutex = new std::recursive_mutex();
 
		if (_draw_mutex == nullptr) {
 
			_draw_threaded = false;
 
		} else {
 
			draw_lock = std::unique_lock<std::recursive_mutex>(*_draw_mutex);
 
			this->draw_lock = std::unique_lock<std::recursive_mutex>(*_draw_mutex);
 
			_draw_signal = new std::condition_variable_any();
 
			_draw_continue = true;
 

	
 
			_draw_threaded = StartNewThread(&draw_thread, "ottd:draw-sdl", &DrawSurfaceToScreenThread);
 

	
 
			/* 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;
 
				_draw_signal = nullptr;
 
			} else {
 
				/* Wait till the draw mutex has started itself. */
 
@@ -760,15 +759,15 @@ void VideoDriver_SDL::MainLoop()
 
				/* Avoid next_game_tick getting behind more and more if it cannot keep up. */
 
				if (next_game_tick < cur_ticks - ALLOWED_DRIFT * this->GetGameInterval()) next_game_tick = cur_ticks;
 
			}
 

	
 
			/* 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. */
 
		if (cur_ticks >= next_draw_tick && (_switch_mode == SM_NONE || HasModalProgress())) {
 
			next_draw_tick += this->GetDrawInterval();
 
			/* Avoid next_draw_tick getting behind more and more if it cannot keep up. */
 
@@ -791,26 +790,26 @@ void VideoDriver_SDL::MainLoop()
 
		if (!_fast_forward || _pause_mode) {
 
			/* 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(next_draw_tick, next_game_tick);
 
			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();
 
			}
 
		}
 
	}
 

	
 
	if (_draw_mutex != nullptr) {
 
		_draw_continue = false;
 
		/* 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;
 
		delete _draw_signal;
 

	
 
		_draw_mutex = nullptr;
 
@@ -855,7 +854,18 @@ void VideoDriver_SDL::AcquireBlitterLock
 

	
 
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 */
src/video/sdl_v.h
Show inline comments
 
@@ -36,14 +36,18 @@ public:
 
	bool ClaimMousePointer() override;
 

	
 
	const char *GetName() const override { return "sdl"; }
 

	
 
protected:
 
	void InputLoop() override;
 
	bool LockVideoBuffer() override;
 
	void UnlockVideoBuffer() override;
 

	
 
private:
 
	std::unique_lock<std::recursive_mutex> draw_lock;
 

	
 
	int PollEvent();
 
	bool CreateMainSurface(uint w, uint h);
 
	void SetupKeyboard();
 
};
 

	
 
/** Factory for the SDL video driver. */
src/video/video_driver.hpp
Show inline comments
 
@@ -158,12 +158,25 @@ protected:
 

	
 
	/**
 
	 * Handle input logic, is CTRL pressed, should we fast-forward, etc.
 
	 */
 
	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);
 
	}
 

	
 
	std::chrono::steady_clock::duration GetDrawInterval()
src/video/win32_v.cpp
Show inline comments
 
@@ -1158,34 +1158,33 @@ void VideoDriver_Win32::MainLoop()
 
	auto cur_ticks = std::chrono::steady_clock::now();
 
	auto last_realtime_tick = cur_ticks;
 
	auto next_game_tick = cur_ticks;
 
	auto next_draw_tick = cur_ticks;
 

	
 
	std::thread draw_thread;
 
	std::unique_lock<std::recursive_mutex> draw_lock;
 

	
 
	if (_draw_threaded) {
 
		/* Initialise the mutex first, because that's the thing we *need*
 
		 * directly in the newly created thread. */
 
		try {
 
			_draw_signal = new std::condition_variable_any();
 
			_draw_mutex = new std::recursive_mutex();
 
		} catch (...) {
 
			_draw_threaded = false;
 
		}
 

	
 
		if (_draw_threaded) {
 
			draw_lock = std::unique_lock<std::recursive_mutex>(*_draw_mutex);
 
			this->draw_lock = std::unique_lock<std::recursive_mutex>(*_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;
 
				_draw_signal = nullptr;
 
			} else {
 
				DEBUG(driver, 1, "Threaded drawing enabled");
 
@@ -1228,15 +1227,15 @@ void VideoDriver_Win32::MainLoop()
 

	
 
			/* Flush GDI buffer to ensure we don't conflict with the drawing thread. */
 
			GdiFlush();
 

	
 
			/* 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. */
 
		if (cur_ticks >= next_draw_tick && (_switch_mode == SM_NONE || HasModalProgress())) {
 
			next_draw_tick += this->GetDrawInterval();
 
			/* Avoid next_draw_tick getting behind more and more if it cannot keep up. */
 
@@ -1266,26 +1265,26 @@ void VideoDriver_Win32::MainLoop()
 
			auto now = std::chrono::steady_clock::now();
 

	
 
			if (next_tick > now) {
 
				/* 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();
 
			}
 
		}
 
	}
 

	
 
	if (_draw_threaded) {
 
		_draw_continue = false;
 
		/* 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;
 
		delete _draw_signal;
 

	
 
		_draw_mutex = nullptr;
 
@@ -1380,6 +1379,17 @@ float VideoDriver_Win32::GetDPIScale()
 
		/* Fall back to system DPI. */
 
		cur_dpi = _GetDpiForSystem();
 
	}
 

	
 
	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();
 
}
src/video/win32_v.h
Show inline comments
 
@@ -42,15 +42,21 @@ public:
 
	bool MakeWindow(bool full_screen);
 

	
 
protected:
 
	Dimension GetScreenSize() const override;
 
	float GetDPIScale() override;
 
	void InputLoop() override;
 
	bool LockVideoBuffer() override;
 
	void UnlockVideoBuffer() override;
 

	
 
private:
 
	std::unique_lock<std::recursive_mutex> draw_lock;
 

	
 
	void CheckPaletteAnim();
 

	
 
	static void PaintThreadThunk(VideoDriver_Win32 *drv);
 
};
 

	
 
/** The factory for Windows' video driver. */
 
class FVideoDriver_Win32 : public DriverFactoryBase {
 
public:
 
	FVideoDriver_Win32() : DriverFactoryBase(Driver::DT_VIDEO, 10, "win32", "Win32 GDI Video Driver") {}
0 comments (0 inline, 0 general)