Changeset - r25011:61d28a13bb41
[Not reviewed]
master
0 14 0
Patric Stout - 3 years ago 2021-02-24 14:04:41
truebrain@openttd.org
Remove: [Video] no longer draw in a thread

Drawing in a thread is a bit odd, and often leads to surprising
issues. For example, OpenGL would only allow it if you move the
full context to the thread. Which is not always easily done on
all OSes.
In general, the advise is to handle system events and drawing
from the main thread, and do everything else in other threads.
So, let's be more like other games.

Additionally, putting the drawing routine in a thread was only
done for a few targets.

Upcoming commit will move the GameLoop in a thread, which will
work for all targets.
14 files changed with 20 insertions and 408 deletions:
0 comments (0 inline, 0 general)
src/video/allegro_v.cpp
Show inline comments
 
@@ -469,27 +469,25 @@ void VideoDriver_Allegro::InputLoop()
 
		(key[KEY_UP]    ? 2 : 0) |
 
		(key[KEY_RIGHT] ? 4 : 0) |
 
		(key[KEY_DOWN]  ? 8 : 0);
 

	
 
	if (old_ctrl_pressed != _ctrl_pressed) HandleCtrlChanged();
 
}
 

	
 
void VideoDriver_Allegro::MainLoop()
 
{
 
	for (;;) {
 
		if (_exit_game) return;
 

	
 
		if (this->Tick()) {
 
			this->Paint();
 
		}
 
		this->Tick();
 
		this->SleepTillNextTick();
 
	}
 
}
 

	
 
bool VideoDriver_Allegro::ChangeResolution(int w, int h)
 
{
 
	return CreateMainSurface(w, h);
 
}
 

	
 
bool VideoDriver_Allegro::ToggleFullscreen(bool fullscreen)
 
{
 
	_fullscreen = fullscreen;
src/video/cocoa/cocoa_v.mm
Show inline comments
 
@@ -434,27 +434,25 @@ void VideoDriver_Cocoa::InputLoop()
 

	
 
/** Main game loop. */
 
void VideoDriver_Cocoa::MainLoopReal()
 
{
 
	for (;;) {
 
		@autoreleasepool {
 
			if (_exit_game) {
 
				/* Restore saved resolution if in fullscreen mode. */
 
				if (this->IsFullscreen()) _cur_resolution = this->orig_res;
 
				break;
 
			}
 

	
 
			if (this->Tick()) {
 
				this->Paint();
 
			}
 
			this->Tick();
 
			this->SleepTillNextTick();
 
		}
 
	}
 
}
 

	
 

	
 
/* Subclass of OTTD_CocoaView to fix Quartz rendering */
 
@interface OTTD_QuartzView : NSView {
 
	VideoDriver_CocoaQuartz *driver;
 
}
 
- (instancetype)initWithFrame:(NSRect)frameRect andDriver:(VideoDriver_CocoaQuartz *)drv;
 
@end
src/video/sdl2_default_v.cpp
Show inline comments
 
@@ -127,40 +127,24 @@ void VideoDriver_SDL_Default::Paint()
 
	}
 

	
 
	SDL_Rect r = { this->dirty_rect.left, this->dirty_rect.top, this->dirty_rect.right - this->dirty_rect.left, this->dirty_rect.bottom - this->dirty_rect.top };
 

	
 
	if (_sdl_surface != _sdl_real_surface) {
 
		SDL_BlitSurface(_sdl_surface, &r, _sdl_real_surface, &r);
 
	}
 
	SDL_UpdateWindowSurfaceRects(this->sdl_window, &r, 1);
 

	
 
	this->dirty_rect = {};
 
}
 

	
 
void VideoDriver_SDL_Default::PaintThread()
 
{
 
	/* First tell the main thread we're started */
 
	std::unique_lock<std::recursive_mutex> lock(*this->draw_mutex);
 
	this->draw_signal->notify_one();
 

	
 
	/* Now wait for the first thing to draw! */
 
	this->draw_signal->wait(*this->draw_mutex);
 

	
 
	while (this->draw_continue) {
 
		/* Then just draw and wait till we stop */
 
		this->Paint();
 
		this->draw_signal->wait(lock);
 
	}
 
}
 

	
 
bool VideoDriver_SDL_Default::AllocateBackingStore(int w, int h, bool force)
 
{
 
	int bpp = BlitterFactory::GetCurrentBlitter()->GetScreenDepth();
 

	
 
	_sdl_real_surface = SDL_GetWindowSurface(this->sdl_window);
 
	if (_sdl_real_surface == nullptr) usererror("SDL2: Couldn't get window surface: %s", SDL_GetError());
 

	
 
	if (!force && w == _sdl_real_surface->w && h == _sdl_real_surface->h) return false;
 

	
 
	/* Free any previously allocated rgb surface. */
 
	if (_sdl_rgb_surface != nullptr) {
 
		SDL_FreeSurface(_sdl_rgb_surface);
src/video/sdl2_default_v.h
Show inline comments
 
@@ -12,25 +12,24 @@
 

	
 
#include "sdl2_v.h"
 

	
 
/** The SDL video driver using default SDL backend. */
 
class VideoDriver_SDL_Default : public VideoDriver_SDL_Base {
 
public:
 
	const char *GetName() const override { return "sdl"; }
 

	
 
protected:
 
	bool AllocateBackingStore(int w, int h, bool force = false) override;
 
	void *GetVideoPointer() override;
 
	void Paint() override;
 
	void PaintThread() override;
 

	
 
	void ReleaseVideoPointer() override {}
 

	
 
private:
 
	void UpdatePalette();
 
	void MakePalette();
 
};
 

	
 
/** Factory for the SDL video driver. */
 
class FVideoDriver_SDL_Default : public DriverFactoryBase {
 
public:
 
	FVideoDriver_SDL_Default() : DriverFactoryBase(Driver::DT_VIDEO, 5, "sdl", "SDL Video Driver") {}
src/video/sdl2_opengl_v.cpp
Show inline comments
 
@@ -62,25 +62,24 @@ const char *VideoDriver_SDL_OpenGL::Star
 
	if (error != nullptr) {
 
		this->Stop();
 
		return error;
 
	}
 

	
 
	/* Now we have a OpenGL context, force a client-size-changed event,
 
	 * so all buffers are allocated correctly. */
 
	int w, h;
 
	SDL_GetWindowSize(this->sdl_window, &w, &h);
 
	this->ClientSizeChanged(w, h, true);
 

	
 
	SDL_GL_SetSwapInterval(GetDriverParamBool(param, "vsync") ? 1 : 0);
 
	this->draw_threaded = false;
 

	
 
	return nullptr;
 
}
 

	
 
void VideoDriver_SDL_OpenGL::Stop()
 
{
 
	this->DestroyContext();
 
	this->VideoDriver_SDL_Base::Stop();
 
}
 

	
 
void VideoDriver_SDL_OpenGL::DestroyContext()
 
{
src/video/sdl2_opengl_v.h
Show inline comments
 
@@ -27,26 +27,24 @@ public:
 
	bool HasAnimBuffer() override { return true; }
 
	uint8 *GetAnimBuffer() override { return this->anim_buffer; }
 

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

	
 
protected:
 
	bool AllocateBackingStore(int w, int h, bool force = false) override;
 
	void *GetVideoPointer() override;
 
	void ReleaseVideoPointer() override;
 
	void Paint() override;
 
	bool CreateMainWindow(uint w, uint h, uint flags) override;
 

	
 
	void PaintThread() override {}
 

	
 
private:
 
	void  *gl_context;  ///< OpenGL context.
 
	uint8 *anim_buffer; ///< Animation buffer from OpenGL back-end.
 

	
 
	const char *AllocateContext();
 
	void DestroyContext();
 
};
 

	
 
/** The factory for SDL' OpenGL video driver. */
 
class FVideoDriver_SDL_OpenGL : public DriverFactoryBase {
 
public:
 
	FVideoDriver_SDL_OpenGL() : DriverFactoryBase(Driver::DT_VIDEO, 8, "sdl-opengl", "SDL OpenGL Video Driver") {}
src/video/sdl2_v.cpp
Show inline comments
 
@@ -14,25 +14,24 @@
 
#include "../blitter/factory.hpp"
 
#include "../thread.h"
 
#include "../progress.h"
 
#include "../core/random_func.hpp"
 
#include "../core/math_func.hpp"
 
#include "../core/mem_func.hpp"
 
#include "../core/geometry_func.hpp"
 
#include "../fileio_func.h"
 
#include "../framerate_type.h"
 
#include "../window_func.h"
 
#include "sdl2_v.h"
 
#include <SDL.h>
 
#include <mutex>
 
#ifdef __EMSCRIPTEN__
 
#	include <emscripten.h>
 
#	include <emscripten/html5.h>
 
#endif
 

	
 
#include "../safeguards.h"
 

	
 
#ifdef __EMSCRIPTEN__
 
/** Whether we just had a window-enter event. */
 
static bool _cursor_new_in_window = false;
 
#endif
 

	
 
@@ -41,29 +40,24 @@ void VideoDriver_SDL_Base::MakeDirty(int
 
	Rect r = {left, top, left + width, top + height};
 
	this->dirty_rect = BoundingRect(this->dirty_rect, r);
 
}
 

	
 
void VideoDriver_SDL_Base::CheckPaletteAnim()
 
{
 
	if (_cur_palette.count_dirty == 0) return;
 

	
 
	this->local_palette = _cur_palette;
 
	this->MakeDirty(0, 0, _screen.width, _screen.height);
 
}
 

	
 
/* static */ void VideoDriver_SDL_Base::PaintThreadThunk(VideoDriver_SDL_Base *drv)
 
{
 
	drv->PaintThread();
 
}
 

	
 
static const Dimension default_resolutions[] = {
 
	{  640,  480 },
 
	{  800,  600 },
 
	{ 1024,  768 },
 
	{ 1152,  864 },
 
	{ 1280,  800 },
 
	{ 1280,  960 },
 
	{ 1280, 1024 },
 
	{ 1400, 1050 },
 
	{ 1600, 1200 },
 
	{ 1680, 1050 },
 
	{ 1920, 1200 }
 
@@ -556,37 +550,24 @@ const char *VideoDriver_SDL_Base::Start(
 

	
 
	this->startup_display = FindStartupDisplay(GetDriverParamInt(parm, "display", -1));
 

	
 
	if (!CreateMainSurface(_cur_resolution.width, _cur_resolution.height, false)) {
 
		return SDL_GetError();
 
	}
 

	
 
	const char *dname = SDL_GetCurrentVideoDriver();
 
	DEBUG(driver, 1, "SDL2: using driver '%s'", dname);
 

	
 
	MarkWholeScreenDirty();
 

	
 
	this->draw_threaded = !GetDriverParamBool(parm, "no_threads") && !GetDriverParamBool(parm, "no_thread");
 
	/* Wayland SDL video driver uses EGL to render the game. SDL created the
 
	 * EGL context from the main-thread, and with EGL you are not allowed to
 
	 * draw in another thread than the context was created. The function of
 
	 * draw_threaded is to do exactly this: draw in another thread than the
 
	 * window was created, and as such, this fails on Wayland SDL video
 
	 * driver. So, we disable threading by default if Wayland SDL video
 
	 * driver is detected.
 
	 */
 
	if (strcmp(dname, "wayland") == 0) {
 
		this->draw_threaded = false;
 
	}
 

	
 
	SDL_StopTextInput();
 
	this->edit_box_focused = false;
 

	
 
	return nullptr;
 
}
 

	
 
void VideoDriver_SDL_Base::Stop()
 
{
 
	SDL_QuitSubSystem(SDL_INIT_VIDEO);
 
	if (SDL_WasInit(SDL_INIT_EVERYTHING) == 0) {
 
		SDL_Quit(); // If there's nothing left, quit SDL
 
	}
 
@@ -622,129 +603,62 @@ void VideoDriver_SDL_Base::InputLoop()
 

	
 
void VideoDriver_SDL_Base::LoopOnce()
 
{
 
	if (_exit_game) {
 
#ifdef __EMSCRIPTEN__
 
		/* Emscripten is event-driven, and as such the main loop is inside
 
		 * the browser. So if _exit_game goes true, the main loop ends (the
 
		 * cancel call), but we still have to call the cleanup that is
 
		 * normally done at the end of the main loop for non-Emscripten.
 
		 * After that, Emscripten just halts, and the HTML shows a nice
 
		 * "bye, see you next time" message. */
 
		emscripten_cancel_main_loop();
 
		MainLoopCleanup();
 
		emscripten_exit_pointerlock();
 
		/* In effect, the game ends here. As emscripten_set_main_loop() caused
 
		 * the stack to be unwound, the code after MainLoop() in
 
		 * openttd_main() is never executed. */
 
		EM_ASM(if (window["openttd_syncfs"]) openttd_syncfs());
 
		EM_ASM(if (window["openttd_exit"]) openttd_exit());
 
#endif
 
		return;
 
	}
 

	
 
	if (VideoDriver::Tick()) {
 
		if (this->draw_mutex != nullptr && !HasModalProgress()) {
 
			this->draw_signal->notify_one();
 
		} else {
 
			this->Paint();
 
		}
 
	}
 
	this->Tick();
 

	
 
/* Emscripten is running an event-based mainloop; there is already some
 
 * downtime between each iteration, so no need to sleep. */
 
#ifndef __EMSCRIPTEN__
 
	this->SleepTillNextTick();
 
#endif
 
}
 

	
 
void VideoDriver_SDL_Base::MainLoop()
 
{
 
	if (this->draw_threaded) {
 
		/* Initialise the mutex first, because that's the thing we *need*
 
		 * directly in the newly created thread. */
 
		this->draw_mutex = new std::recursive_mutex();
 
		if (this->draw_mutex == nullptr) {
 
			this->draw_threaded = false;
 
		} else {
 
			draw_lock = std::unique_lock<std::recursive_mutex>(*this->draw_mutex);
 
			this->draw_signal = new std::condition_variable_any();
 
			this->draw_continue = true;
 

	
 
			this->draw_threaded = StartNewThread(&draw_thread, "ottd:draw-sdl", &VideoDriver_SDL_Base::PaintThreadThunk, this);
 

	
 
			/* Free the mutex if we won't be able to use it. */
 
			if (!this->draw_threaded) {
 
				draw_lock.unlock();
 
				draw_lock.release();
 
				delete this->draw_mutex;
 
				delete this->draw_signal;
 
				this->draw_mutex = nullptr;
 
				this->draw_signal = nullptr;
 
			} else {
 
				/* Wait till the draw mutex has started itself. */
 
				this->draw_signal->wait(*this->draw_mutex);
 
			}
 
		}
 
	}
 

	
 
	DEBUG(driver, 1, "SDL2: using %sthreads", this->draw_threaded ? "" : "no ");
 

	
 
#ifdef __EMSCRIPTEN__
 
	/* Run the main loop event-driven, based on RequestAnimationFrame. */
 
	emscripten_set_main_loop_arg(&this->EmscriptenLoop, this, 0, 1);
 
#else
 
	while (!_exit_game) {
 
		LoopOnce();
 
	}
 

	
 
	MainLoopCleanup();
 
#endif
 
}
 

	
 
void VideoDriver_SDL_Base::MainLoopCleanup()
 
{
 
	if (this->draw_mutex != nullptr) {
 
		this->draw_continue = false;
 
		/* Sending signal if there is no thread blocked
 
		 * is very valid and results in noop */
 
		this->draw_signal->notify_one();
 
		if (draw_lock.owns_lock()) draw_lock.unlock();
 
		draw_lock.release();
 
		draw_thread.join();
 

	
 
		delete this->draw_mutex;
 
		delete this->draw_signal;
 

	
 
		this->draw_mutex = nullptr;
 
		this->draw_signal = nullptr;
 
	}
 

	
 
#ifdef __EMSCRIPTEN__
 
	emscripten_exit_pointerlock();
 
	/* In effect, the game ends here. As emscripten_set_main_loop() caused
 
	 * the stack to be unwound, the code after MainLoop() in
 
	 * openttd_main() is never executed. */
 
	EM_ASM(if (window["openttd_syncfs"]) openttd_syncfs());
 
	EM_ASM(if (window["openttd_exit"]) openttd_exit());
 
#endif
 
}
 

	
 
bool VideoDriver_SDL_Base::ChangeResolution(int w, int h)
 
{
 
	std::unique_lock<std::recursive_mutex> lock;
 
	if (this->draw_mutex != nullptr) lock = std::unique_lock<std::recursive_mutex>(*this->draw_mutex);
 

	
 
	return CreateMainSurface(w, h, true);
 
}
 

	
 
bool VideoDriver_SDL_Base::ToggleFullscreen(bool fullscreen)
 
{
 
	std::unique_lock<std::recursive_mutex> lock;
 
	if (this->draw_mutex != nullptr) lock = std::unique_lock<std::recursive_mutex>(*this->draw_mutex);
 

	
 
	int w, h;
 

	
 
	/* Remember current window size */
 
	if (fullscreen) {
 
		SDL_GetWindowSize(this->sdl_window, &w, &h);
 

	
 
		/* Find fullscreen window size */
 
		SDL_DisplayMode dm;
 
		if (SDL_GetCurrentDisplayMode(0, &dm) < 0) {
 
			DEBUG(driver, 0, "SDL_GetCurrentDisplayMode() failed: %s", SDL_GetError());
 
		} else {
 
			SDL_SetWindowSize(this->sdl_window, dm.w, dm.h);
 
@@ -764,54 +678,41 @@ bool VideoDriver_SDL_Base::ToggleFullscr
 
	InvalidateWindowClassesData(WC_GAME_OPTIONS, 3);
 
	return ret == 0;
 
}
 

	
 
bool VideoDriver_SDL_Base::AfterBlitterChange()
 
{
 
	assert(BlitterFactory::GetCurrentBlitter()->GetScreenDepth() != 0);
 
	int w, h;
 
	SDL_GetWindowSize(this->sdl_window, &w, &h);
 
	return CreateMainSurface(w, h, false);
 
}
 

	
 
void VideoDriver_SDL_Base::AcquireBlitterLock()
 
{
 
	if (this->draw_mutex != nullptr) this->draw_mutex->lock();
 
}
 

	
 
void VideoDriver_SDL_Base::ReleaseBlitterLock()
 
{
 
	if (this->draw_mutex != nullptr) this->draw_mutex->unlock();
 
}
 

	
 
Dimension VideoDriver_SDL_Base::GetScreenSize() const
 
{
 
	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_Base::LockVideoBuffer()
 
{
 
	if (this->buffer_locked) return false;
 
	this->buffer_locked = true;
 

	
 
	if (this->draw_threaded) this->draw_lock.lock();
 

	
 
	_screen.dst_ptr = this->GetVideoPointer();
 
	assert(_screen.dst_ptr != nullptr);
 

	
 
	return true;
 
}
 

	
 
void VideoDriver_SDL_Base::UnlockVideoBuffer()
 
{
 
	if (_screen.dst_ptr != nullptr) {
 
		/* Hand video buffer back to the drawing backend. */
 
		this->ReleaseVideoPointer();
 
		_screen.dst_ptr = nullptr;
 
	}
 

	
 
	if (this->draw_threaded) this->draw_lock.unlock();
 
	this->buffer_locked = false;
 
}
src/video/sdl2_v.h
Show inline comments
 
@@ -24,43 +24,35 @@ public:
 
	void Stop() override;
 

	
 
	void MakeDirty(int left, int top, int width, int height) override;
 

	
 
	void MainLoop() override;
 

	
 
	bool ChangeResolution(int w, int h) override;
 

	
 
	bool ToggleFullscreen(bool fullscreen) override;
 

	
 
	bool AfterBlitterChange() override;
 

	
 
	void AcquireBlitterLock() override;
 

	
 
	void ReleaseBlitterLock() override;
 

	
 
	bool ClaimMousePointer() override;
 

	
 
	void EditBoxGainedFocus() override;
 

	
 
	void EditBoxLostFocus() override;
 

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

	
 
protected:
 
	struct SDL_Window *sdl_window; ///< Main SDL window.
 
	Palette local_palette; ///< Copy of _cur_palette.
 
	bool draw_threaded; ///< Whether the drawing is/may be done in a separate thread.
 
	std::recursive_mutex *draw_mutex = nullptr; ///< Mutex to keep the access to the shared memory controlled.
 
	std::condition_variable_any *draw_signal = nullptr; ///< Signal to draw the next frame.
 
	volatile bool draw_continue; ///< Should we keep continue drawing?
 
	bool buffer_locked; ///< Video buffer was locked by the main thread.
 
	Rect dirty_rect; ///< Rectangle encompassing the dirty area of the video buffer.
 

	
 
	Dimension GetScreenSize() const override;
 
	void InputLoop() override;
 
	bool LockVideoBuffer() override;
 
	void UnlockVideoBuffer() override;
 
	void CheckPaletteAnim() override;
 
	bool PollEvent() override;
 

	
 
	/** Indicate to the driver the client-side might have changed. */
 
	void ClientSizeChanged(int w, int h, bool force);
 
@@ -82,19 +74,15 @@ private:
 

	
 
#ifdef __EMSCRIPTEN__
 
	/* Convert a constant pointer back to a non-constant pointer to a member function. */
 
	static void EmscriptenLoop(void *self) { ((VideoDriver_SDL_Base *)self)->LoopOnce(); }
 
#endif
 

	
 
	/**
 
	 * This is true to indicate that keyboard input is in text input mode, and SDL_TEXTINPUT events are enabled.
 
	 */
 
	bool edit_box_focused;
 

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

	
 
	static void PaintThreadThunk(VideoDriver_SDL_Base *drv);
 
};
 

	
 
#endif /* VIDEO_SDL_H */
src/video/sdl_v.cpp
Show inline comments
 
@@ -14,43 +14,33 @@
 
#include "../gfx_func.h"
 
#include "../rev.h"
 
#include "../blitter/factory.hpp"
 
#include "../thread.h"
 
#include "../progress.h"
 
#include "../core/random_func.hpp"
 
#include "../core/math_func.hpp"
 
#include "../fileio_func.h"
 
#include "../framerate_type.h"
 
#include "../window_func.h"
 
#include "sdl_v.h"
 
#include <SDL.h>
 
#include <mutex>
 
#include <condition_variable>
 

	
 
#include "../safeguards.h"
 

	
 
static FVideoDriver_SDL iFVideoDriver_SDL;
 

	
 
static SDL_Surface *_sdl_surface;
 
static SDL_Surface *_sdl_realscreen;
 
static bool _all_modes;
 

	
 
/** Whether the drawing is/may be done in a separate thread. */
 
static bool _draw_threaded;
 
/** Mutex to keep the access to the shared memory controlled. */
 
static std::recursive_mutex *_draw_mutex = nullptr;
 
/** Signal to draw the next frame. */
 
static std::condition_variable_any *_draw_signal = nullptr;
 
/** Should we keep continue drawing? */
 
static volatile bool _draw_continue;
 
static Palette _local_palette;
 

	
 
#define MAX_DIRTY_RECTS 100
 
static SDL_Rect _dirty_rects[MAX_DIRTY_RECTS];
 
static int _num_dirty_rects;
 
static int _use_hwpalette;
 
static int _requested_hwpalette; /* Did we request a HWPALETTE for the current video mode? */
 

	
 
void VideoDriver_SDL::MakeDirty(int left, int top, int width, int height)
 
{
 
	if (_num_dirty_rects < MAX_DIRTY_RECTS) {
 
		_dirty_rects[_num_dirty_rects].x = left;
 
@@ -165,45 +155,24 @@ void VideoDriver_SDL::Paint()
 
		SDL_UpdateRect(_sdl_realscreen, 0, 0, 0, 0);
 
	} else {
 
		if (_sdl_surface != _sdl_realscreen) {
 
			for (int i = 0; i < n; i++) {
 
				SDL_BlitSurface(_sdl_surface, &_dirty_rects[i], _sdl_realscreen, &_dirty_rects[i]);
 
			}
 
		}
 

	
 
		SDL_UpdateRects(_sdl_realscreen, n, _dirty_rects);
 
	}
 
}
 

	
 
void VideoDriver_SDL::PaintThread()
 
{
 
	/* First tell the main thread we're started */
 
	std::unique_lock<std::recursive_mutex> lock(*_draw_mutex);
 
	_draw_signal->notify_one();
 

	
 
	/* Now wait for the first thing to draw! */
 
	_draw_signal->wait(*_draw_mutex);
 

	
 
	while (_draw_continue) {
 
		/* Then just draw and wait till we stop */
 
		this->Paint();
 
		_draw_signal->wait(lock);
 
	}
 
}
 

	
 
/* static */ void VideoDriver_SDL::PaintThreadThunk(VideoDriver_SDL *drv)
 
{
 
	drv->PaintThread();
 
}
 

	
 
static const Dimension _default_resolutions[] = {
 
	{ 640,  480},
 
	{ 800,  600},
 
	{1024,  768},
 
	{1152,  864},
 
	{1280,  800},
 
	{1280,  960},
 
	{1280, 1024},
 
	{1400, 1050},
 
	{1600, 1200},
 
	{1680, 1050},
 
	{1920, 1200}
 
@@ -621,26 +590,24 @@ const char *VideoDriver_SDL::Start(const
 

	
 
	GetVideoModes();
 
	if (!CreateMainSurface(_cur_resolution.width, _cur_resolution.height)) {
 
		return SDL_GetError();
 
	}
 

	
 
	SDL_VideoDriverName(buf, sizeof buf);
 
	DEBUG(driver, 1, "SDL: using driver '%s'", buf);
 

	
 
	MarkWholeScreenDirty();
 
	SetupKeyboard();
 

	
 
	_draw_threaded = !GetDriverParamBool(parm, "no_threads") && !GetDriverParamBool(parm, "no_thread");
 

	
 
	return nullptr;
 
}
 

	
 
void VideoDriver_SDL::SetupKeyboard()
 
{
 
	SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
 
	SDL_EnableUNICODE(1);
 
}
 

	
 
void VideoDriver_SDL::Stop()
 
{
 
	SDL_QuitSubSystem(SDL_INIT_VIDEO);
 
@@ -671,126 +638,46 @@ void VideoDriver_SDL::InputLoop()
 
	/* Determine which directional keys are down. */
 
	_dirkeys =
 
		(keys[SDLK_LEFT]  ? 1 : 0) |
 
		(keys[SDLK_UP]    ? 2 : 0) |
 
		(keys[SDLK_RIGHT] ? 4 : 0) |
 
		(keys[SDLK_DOWN]  ? 8 : 0);
 

	
 
	if (old_ctrl_pressed != _ctrl_pressed) HandleCtrlChanged();
 
}
 

	
 
void VideoDriver_SDL::MainLoop()
 
{
 
	std::thread draw_thread;
 
	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 {
 
			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", &VideoDriver_SDL::PaintThreadThunk, this);
 

	
 
			/* Free the mutex if we won't be able to use it. */
 
			if (!_draw_threaded) {
 
				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. */
 
				_draw_signal->wait(*_draw_mutex);
 
			}
 
		}
 
	}
 

	
 
	DEBUG(driver, 1, "SDL: using %sthreads", _draw_threaded ? "" : "no ");
 

	
 
	for (;;) {
 
		if (_exit_game) break;
 

	
 
		if (this->Tick()) {
 
			if (_draw_mutex != nullptr && !HasModalProgress()) {
 
				_draw_signal->notify_one();
 
			} else {
 
				this->Paint();
 
			}
 
		}
 
		this->Tick();
 
		this->SleepTillNextTick();
 
	}
 

	
 
	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 (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;
 
		_draw_signal = nullptr;
 
	}
 
}
 

	
 
bool VideoDriver_SDL::ChangeResolution(int w, int h)
 
{
 
	std::unique_lock<std::recursive_mutex> lock;
 
	if (_draw_mutex != nullptr) lock = std::unique_lock<std::recursive_mutex>(*_draw_mutex);
 

	
 
	return CreateMainSurface(w, h);
 
}
 

	
 
bool VideoDriver_SDL::ToggleFullscreen(bool fullscreen)
 
{
 
	std::unique_lock<std::recursive_mutex> lock;
 
	if (_draw_mutex != nullptr) lock = std::unique_lock<std::recursive_mutex>(*_draw_mutex);
 

	
 
	_fullscreen = fullscreen;
 
	GetVideoModes(); // get the list of available video modes
 
	bool ret = !_resolutions.empty() && CreateMainSurface(_cur_resolution.width, _cur_resolution.height);
 

	
 
	if (!ret) {
 
		/* switching resolution failed, put back full_screen to original status */
 
		_fullscreen ^= true;
 
	}
 

	
 
	InvalidateWindowClassesData(WC_GAME_OPTIONS, 3);
 
	return ret;
 
}
 

	
 
bool VideoDriver_SDL::AfterBlitterChange()
 
{
 
	return CreateMainSurface(_screen.width, _screen.height);
 
}
 

	
 
void VideoDriver_SDL::AcquireBlitterLock()
 
{
 
	if (_draw_mutex != nullptr) _draw_mutex->lock();
 
}
 

	
 
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
 
@@ -20,46 +20,35 @@ public:
 
	void Stop() override;
 

	
 
	void MakeDirty(int left, int top, int width, int height) override;
 

	
 
	void MainLoop() override;
 

	
 
	bool ChangeResolution(int w, int h) override;
 

	
 
	bool ToggleFullscreen(bool fullscreen) override;
 

	
 
	bool AfterBlitterChange() override;
 

	
 
	void AcquireBlitterLock() override;
 

	
 
	void ReleaseBlitterLock() override;
 

	
 
	bool ClaimMousePointer() override;
 

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

	
 
protected:
 
	void InputLoop() override;
 
	bool LockVideoBuffer() override;
 
	void UnlockVideoBuffer() override;
 
	void Paint() override;
 
	void PaintThread() override;
 
	void CheckPaletteAnim() override;
 
	bool PollEvent() override;
 

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

	
 
	bool CreateMainSurface(uint w, uint h);
 
	void SetupKeyboard();
 

	
 
	static void PaintThreadThunk(VideoDriver_SDL *drv);
 
};
 

	
 
/** Factory for the SDL video driver. */
 
class FVideoDriver_SDL : public DriverFactoryBase {
 
public:
 
	FVideoDriver_SDL() : DriverFactoryBase(Driver::DT_VIDEO, 5, "sdl", "SDL Video Driver") {}
 
	Driver *CreateInstance() const override { return new VideoDriver_SDL(); }
 
};
 

	
 
#endif /* VIDEO_SDL_H */
src/video/video_driver.cpp
Show inline comments
 
@@ -10,25 +10,25 @@
 
#include "../stdafx.h"
 
#include "../debug.h"
 
#include "../core/random_func.hpp"
 
#include "../network/network.h"
 
#include "../gfx_func.h"
 
#include "../progress.h"
 
#include "../thread.h"
 
#include "../window_func.h"
 
#include "video_driver.hpp"
 

	
 
bool _video_hw_accel; ///< Whether to consider hardware accelerated video drivers.
 

	
 
bool VideoDriver::Tick()
 
void VideoDriver::Tick()
 
{
 
	auto cur_ticks = std::chrono::steady_clock::now();
 

	
 
	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;
 

	
 
		/* The game loop is the part that can run asynchronously.
 
		 * The rest except sleeping can't. */
 
		this->UnlockVideoBuffer();
 
		::GameLoop();
 
@@ -57,30 +57,28 @@ bool VideoDriver::Tick()
 

	
 
		/* Check if the fast-forward button is still pressed. */
 
		if (fast_forward_key_pressed && !_networking && _game_mode != GM_MENU) {
 
			ChangeGameSpeed(true);
 
			this->fast_forward_via_key = true;
 
		} else if (this->fast_forward_via_key) {
 
			ChangeGameSpeed(false);
 
			this->fast_forward_via_key = false;
 
		}
 

	
 
		::InputLoop();
 
		UpdateWindows();
 

	
 
		this->CheckPaletteAnim();
 

	
 
		return true;
 
		this->Paint();
 
	}
 

	
 
	return false;
 
}
 

	
 
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 now = std::chrono::steady_clock::now();
 

	
 
	if (next_tick > now) {
 
		this->UnlockVideoBuffer();
 
		std::this_thread::sleep_for(next_tick - now);
 
		this->LockVideoBuffer();
src/video/video_driver.hpp
Show inline comments
 
@@ -233,44 +233,40 @@ protected:
 

	
 
	/**
 
	 * Unlock a previously locked video buffer.
 
	 */
 
	virtual void UnlockVideoBuffer() {}
 

	
 
	/**
 
	 * Paint the window.
 
	 */
 
	virtual void Paint() {}
 

	
 
	/**
 
	 * Thread function for threaded drawing.
 
	 */
 
	virtual void PaintThread() {}
 

	
 
	/**
 
	 * Process any pending palette animation.
 
	 */
 
	virtual void CheckPaletteAnim() {}
 

	
 
	/**
 
	 * Process a single system event.
 
	 * @returns False if there are no more events to process.
 
	 */
 
	virtual bool PollEvent() { return false; };
 

	
 
	/**
 
	 * Run the game for a single tick, processing boththe game-tick and draw-tick.
 
	 * @returns True if the driver should redraw the screen.
 
	 * 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.
 
	 */
 
	bool Tick();
 
	void Tick();
 

	
 
	/**
 
	 * Sleep till the next tick is about to happen.
 
	 */
 
	void SleepTillNextTick();
 

	
 
	std::chrono::steady_clock::duration GetGameInterval()
 
	{
 
		/* If we are paused, run on normal speed. */
 
		if (_pause_mode) return std::chrono::milliseconds(MILLISECONDS_PER_TICK);
 
		/* Infinite speed, as quickly as you can. */
 
		if (_game_speed == 0) return std::chrono::microseconds(0);
src/video/win32_v.cpp
Show inline comments
 
@@ -216,29 +216,24 @@ bool VideoDriver_Win32Base::MakeWindow(b
 
			this->main_wnd = CreateWindow(L"OTTD", OTTD2FS(window_title), style, x, y, w, h, 0, 0, GetModuleHandle(nullptr), this);
 
			if (this->main_wnd == nullptr) usererror("CreateWindow failed");
 
			ShowWindow(this->main_wnd, showstyle);
 
		}
 
	}
 

	
 
	BlitterFactory::GetCurrentBlitter()->PostResize();
 

	
 
	GameSizeChanged();
 
	return true;
 
}
 

	
 
/* static */ void VideoDriver_Win32Base::PaintThreadThunk(VideoDriver_Win32Base *drv)
 
{
 
	drv->PaintThread();
 
}
 

	
 
/** Forward key presses to the window system. */
 
static LRESULT HandleCharMsg(uint keycode, WChar charcode)
 
{
 
	static WChar prev_char = 0;
 

	
 
	/* Did we get a lead surrogate? If yes, store and exit. */
 
	if (Utf16IsLeadSurrogate(charcode)) {
 
		if (prev_char != 0) DEBUG(driver, 1, "Got two UTF-16 lead surrogates, dropping the first one");
 
		prev_char = charcode;
 
		return 0;
 
	}
 

	
 
@@ -859,144 +854,67 @@ bool VideoDriver_Win32Base::PollEvent()
 

	
 
	if (!PeekMessage(&mesg, nullptr, 0, 0, PM_REMOVE)) return false;
 

	
 
	/* Convert key messages to char messages if we want text input. */
 
	if (EditBoxInGlobalFocus()) TranslateMessage(&mesg);
 
	DispatchMessage(&mesg);
 

	
 
	return true;
 
}
 

	
 
void VideoDriver_Win32Base::MainLoop()
 
{
 
	std::thread draw_thread;
 

	
 
	if (this->draw_threaded) {
 
		/* Initialise the mutex first, because that's the thing we *need*
 
		 * directly in the newly created thread. */
 
		try {
 
			this->draw_signal = new std::condition_variable_any();
 
			this->draw_mutex = new std::recursive_mutex();
 
		} catch (...) {
 
			this->draw_threaded = false;
 
		}
 

	
 
		if (this->draw_threaded) {
 
			this->draw_lock = std::unique_lock<std::recursive_mutex>(*this->draw_mutex);
 

	
 
			this->draw_continue = true;
 
			this->draw_threaded = StartNewThread(&draw_thread, "ottd:draw-win32", &VideoDriver_Win32Base::PaintThreadThunk, this);
 

	
 
			/* Free the mutex if we won't be able to use it. */
 
			if (!this->draw_threaded) {
 
				this->draw_lock.unlock();
 
				this->draw_lock.release();
 
				delete this->draw_mutex;
 
				delete this->draw_signal;
 
				this->draw_mutex = nullptr;
 
				this->draw_signal = nullptr;
 
			} else {
 
				DEBUG(driver, 1, "Threaded drawing enabled");
 
				/* Wait till the draw thread has started itself. */
 
				this->draw_signal->wait(*this->draw_mutex);
 
			}
 
		}
 
	}
 

	
 
	for (;;) {
 
		if (_exit_game) break;
 

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

	
 
		if (this->Tick()) {
 
			if (this->draw_mutex != nullptr && !HasModalProgress()) {
 
				this->draw_signal->notify_one();
 
			} else {
 
				this->Paint();
 
			}
 
		}
 
		this->Tick();
 
		this->SleepTillNextTick();
 
	}
 

	
 
	if (this->draw_threaded) {
 
		this->draw_continue = false;
 
		/* Sending signal if there is no thread blocked
 
		 * is very valid and results in noop */
 
		this->draw_signal->notify_all();
 
		if (this->draw_lock.owns_lock()) this->draw_lock.unlock();
 
		this->draw_lock.release();
 
		draw_thread.join();
 

	
 
		delete this->draw_mutex;
 
		delete this->draw_signal;
 

	
 
		this->draw_mutex = nullptr;
 
	}
 
}
 

	
 
void VideoDriver_Win32Base::ClientSizeChanged(int w, int h, bool force)
 
{
 
	/* Allocate backing store of the new size. */
 
	if (this->AllocateBackingStore(w, h, force)) {
 
		/* Mark all palette colours dirty. */
 
		_cur_palette.first_dirty = 0;
 
		_cur_palette.count_dirty = 256;
 
		_local_palette = _cur_palette;
 

	
 
		BlitterFactory::GetCurrentBlitter()->PostResize();
 

	
 
		GameSizeChanged();
 
	}
 
}
 

	
 
bool VideoDriver_Win32Base::ChangeResolution(int w, int h)
 
{
 
	std::unique_lock<std::recursive_mutex> lock;
 
	if (this->draw_mutex != nullptr) lock = std::unique_lock<std::recursive_mutex>(*this->draw_mutex);
 

	
 
	if (_window_maximize) ShowWindow(this->main_wnd, SW_SHOWNORMAL);
 

	
 
	this->width = this->width_org = w;
 
	this->height = this->height_org = h;
 

	
 
	return this->MakeWindow(_fullscreen); // _wnd.fullscreen screws up ingame resolution switching
 
}
 

	
 
bool VideoDriver_Win32Base::ToggleFullscreen(bool full_screen)
 
{
 
	std::unique_lock<std::recursive_mutex> lock;
 
	if (this->draw_mutex != nullptr) lock = std::unique_lock<std::recursive_mutex>(*this->draw_mutex);
 

	
 
	bool res = this->MakeWindow(full_screen);
 

	
 
	InvalidateWindowClassesData(WC_GAME_OPTIONS, 3);
 
	return res;
 
}
 

	
 
void VideoDriver_Win32Base::AcquireBlitterLock()
 
{
 
	if (this->draw_mutex != nullptr) this->draw_mutex->lock();
 
}
 

	
 
void VideoDriver_Win32Base::ReleaseBlitterLock()
 
{
 
	if (this->draw_mutex != nullptr) this->draw_mutex->unlock();
 
}
 

	
 
void VideoDriver_Win32Base::EditBoxLostFocus()
 
{
 
	std::unique_lock<std::recursive_mutex> lock;
 
	if (this->draw_mutex != nullptr) lock = std::unique_lock<std::recursive_mutex>(*this->draw_mutex);
 

	
 
	CancelIMEComposition(this->main_wnd);
 
	SetCompositionPos(this->main_wnd);
 
	SetCandidatePos(this->main_wnd);
 
}
 

	
 
Dimension VideoDriver_Win32Base::GetScreenSize() const
 
{
 
	return { static_cast<uint>(GetSystemMetrics(SM_CXSCREEN)), static_cast<uint>(GetSystemMetrics(SM_CYSCREEN)) };
 
}
 

	
 
float VideoDriver_Win32Base::GetDPIScale()
 
{
 
@@ -1034,62 +952,57 @@ float VideoDriver_Win32Base::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_Win32Base::LockVideoBuffer()
 
{
 
	if (this->buffer_locked) return false;
 
	this->buffer_locked = true;
 

	
 
	if (this->draw_threaded) this->draw_lock.lock();
 

	
 
	_screen.dst_ptr = this->GetVideoPointer();
 
	assert(_screen.dst_ptr != nullptr);
 

	
 
	return true;
 
}
 

	
 
void VideoDriver_Win32Base::UnlockVideoBuffer()
 
{
 
	assert(_screen.dst_ptr != nullptr);
 
	if (_screen.dst_ptr != nullptr) {
 
		/* Hand video buffer back to the drawing backend. */
 
		this->ReleaseVideoPointer();
 
		_screen.dst_ptr = nullptr;
 
	}
 

	
 
	if (this->draw_threaded) this->draw_lock.unlock();
 
	this->buffer_locked = false;
 
}
 

	
 

	
 
static FVideoDriver_Win32GDI iFVideoDriver_Win32GDI;
 

	
 
const char *VideoDriver_Win32GDI::Start(const StringList &param)
 
{
 
	if (BlitterFactory::GetCurrentBlitter()->GetScreenDepth() == 0) return "Only real blitters supported";
 

	
 
	this->Initialize();
 

	
 
	this->MakePalette();
 
	this->AllocateBackingStore(_cur_resolution.width, _cur_resolution.height);
 
	this->MakeWindow(_fullscreen);
 

	
 
	MarkWholeScreenDirty();
 

	
 
	this->draw_threaded = !GetDriverParam(param, "no_threads") && !GetDriverParam(param, "no_thread") && std::thread::hardware_concurrency() > 1;
 

	
 
	return nullptr;
 
}
 

	
 
void VideoDriver_Win32GDI::Stop()
 
{
 
	DeleteObject(this->gdi_palette);
 
	DeleteObject(this->dib_sect);
 

	
 
	this->VideoDriver_Win32Base::Stop();
 
}
 

	
 
bool VideoDriver_Win32GDI::AllocateBackingStore(int w, int h, bool force)
 
@@ -1221,44 +1134,24 @@ void VideoDriver_Win32GDI::Paint()
 
	}
 

	
 
	BitBlt(dc, 0, 0, this->width, this->height, dc2, 0, 0, SRCCOPY);
 
	SelectPalette(dc, old_palette, TRUE);
 
	SelectObject(dc2, old_bmp);
 
	DeleteDC(dc2);
 

	
 
	ReleaseDC(this->main_wnd, dc);
 

	
 
	this->dirty_rect = {};
 
}
 

	
 
void VideoDriver_Win32GDI::PaintThread()
 
{
 
	/* First tell the main thread we're started */
 
	std::unique_lock<std::recursive_mutex> lock(*this->draw_mutex);
 
	this->draw_signal->notify_one();
 

	
 
	/* Now wait for the first thing to draw! */
 
	this->draw_signal->wait(*this->draw_mutex);
 

	
 
	while (this->draw_continue) {
 
		this->Paint();
 

	
 
		/* Flush GDI buffer to ensure drawing here doesn't conflict with any GDI usage in the main WndProc. */
 
		GdiFlush();
 

	
 
		this->draw_signal->wait(*this->draw_mutex);
 
	}
 
}
 

	
 

	
 
#ifdef _DEBUG
 
/* Keep this function here..
 
 * It allows you to redraw the screen from within the MSVC debugger */
 
/* static */ int VideoDriver_Win32GDI::RedrawScreenDebug()
 
{
 
	static int _fooctr;
 

	
 
	VideoDriver_Win32GDI *drv = static_cast<VideoDriver_Win32GDI *>(VideoDriver::GetInstance());
 

	
 
	_screen.dst_ptr = drv->GetVideoPointer();
 
	UpdateWindows();
 

	
 
@@ -1386,25 +1279,24 @@ const char *VideoDriver_Win32OpenGL::Sta
 
	this->MakeWindow(_fullscreen);
 

	
 
	/* Create and initialize OpenGL context. */
 
	const char *err = this->AllocateContext();
 
	if (err != nullptr) {
 
		this->Stop();
 
		_cur_resolution = old_res;
 
		return err;
 
	}
 

	
 
	this->ClientSizeChanged(this->width, this->height, true);
 

	
 
	this->draw_threaded = false;
 
	MarkWholeScreenDirty();
 

	
 
	return nullptr;
 
}
 

	
 
void VideoDriver_Win32OpenGL::Stop()
 
{
 
	this->DestroyContext();
 
	this->VideoDriver_Win32Base::Stop();
 
}
 

	
 
void VideoDriver_Win32OpenGL::DestroyContext()
src/video/win32_v.h
Show inline comments
 
@@ -8,60 +8,51 @@
 
/** @file win32_v.h Base of the Windows video driver. */
 

	
 
#ifndef VIDEO_WIN32_H
 
#define VIDEO_WIN32_H
 

	
 
#include "video_driver.hpp"
 
#include <mutex>
 
#include <condition_variable>
 

	
 
/** Base class for Windows video drivers. */
 
class VideoDriver_Win32Base : public VideoDriver {
 
public:
 
	VideoDriver_Win32Base() : main_wnd(nullptr), fullscreen(false), draw_mutex(nullptr), draw_signal(nullptr) {}
 
	VideoDriver_Win32Base() : main_wnd(nullptr), fullscreen(false) {}
 

	
 
	void Stop() override;
 

	
 
	void MakeDirty(int left, int top, int width, int height) override;
 

	
 
	void MainLoop() override;
 

	
 
	bool ChangeResolution(int w, int h) override;
 

	
 
	bool ToggleFullscreen(bool fullscreen) override;
 

	
 
	void AcquireBlitterLock() override;
 

	
 
	void ReleaseBlitterLock() override;
 

	
 
	bool ClaimMousePointer() override;
 

	
 
	void EditBoxLostFocus() override;
 

	
 
protected:
 
	HWND main_wnd;          ///< Handle to system window.
 
	bool fullscreen;        ///< Whether to use (true) fullscreen mode.
 
	bool has_focus = false; ///< Does our window have system focus?
 
	Rect dirty_rect;        ///< Region of the screen that needs redrawing.
 
	int width = 0;          ///< Width in pixels of our display surface.
 
	int height = 0;         ///< Height in pixels of our display surface.
 
	int width_org = 0;      ///< Original monitor resolution width, before we changed it.
 
	int height_org = 0;     ///< Original monitor resolution height, before we changed it.
 

	
 
	bool draw_threaded;          ///< Whether the drawing is/may be done in a separate thread.
 
	bool buffer_locked;          ///< Video buffer was locked by the main thread.
 
	volatile bool draw_continue; ///< Should we keep continue drawing?
 

	
 
	std::recursive_mutex *draw_mutex;         ///< Mutex to keep the access to the shared memory controlled.
 
	std::condition_variable_any *draw_signal; ///< Signal to draw the next frame.
 
	bool buffer_locked;     ///< Video buffer was locked by the main thread.
 

	
 
	Dimension GetScreenSize() const override;
 
	float GetDPIScale() override;
 
	void InputLoop() override;
 
	bool LockVideoBuffer() override;
 
	void UnlockVideoBuffer() override;
 
	void CheckPaletteAnim() override;
 
	bool PollEvent() override;
 

	
 
	void Initialize();
 
	bool MakeWindow(bool full_screen);
 
	void ClientSizeChanged(int w, int h, bool force = false);
 
@@ -69,51 +60,46 @@ protected:
 
	/** Get screen depth to use for fullscreen mode. */
 
	virtual uint8 GetFullscreenBpp();
 
	/** (Re-)create the backing store. */
 
	virtual bool AllocateBackingStore(int w, int h, bool force = false) = 0;
 
	/** Get a pointer to the video buffer. */
 
	virtual void *GetVideoPointer() = 0;
 
	/** Hand video buffer back to the painting backend. */
 
	virtual void ReleaseVideoPointer() {}
 
	/** Palette of the window has changed. */
 
	virtual void PaletteChanged(HWND hWnd) = 0;
 

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

	
 
	static void PaintThreadThunk(VideoDriver_Win32Base *drv);
 

	
 
	friend LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
 
};
 
/** The GDI video driver for windows. */
 
class VideoDriver_Win32GDI : public VideoDriver_Win32Base {
 
public:
 
	VideoDriver_Win32GDI() : dib_sect(nullptr), gdi_palette(nullptr), buffer_bits(nullptr) {}
 

	
 
	const char *Start(const StringList &param) override;
 

	
 
	void Stop() override;
 

	
 
	bool AfterBlitterChange() override;
 

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

	
 
protected:
 
	HBITMAP  dib_sect;      ///< System bitmap object referencing our rendering buffer.
 
	HPALETTE gdi_palette;   ///< Palette object for 8bpp blitter.
 
	void     *buffer_bits;  ///< Internal rendering buffer.
 

	
 
	void Paint() override;
 
	void *GetVideoPointer() override { return this->buffer_bits; }
 
	void PaintThread() override;
 

	
 
	bool AllocateBackingStore(int w, int h, bool force = false) override;
 
	void PaletteChanged(HWND hWnd) override;
 
	void MakePalette();
 
	void UpdatePalette(HDC dc, uint start, uint count);
 

	
 
#ifdef _DEBUG
 
public:
 
	static int RedrawScreenDebug();
 
#endif
 
};
 

	
 
@@ -150,25 +136,24 @@ public:
 

	
 
	const char *GetName() const override { return "win32-opengl"; }
 

	
 
protected:
 
	HDC    dc;          ///< Window device context.
 
	HGLRC  gl_rc;       ///< OpenGL context.
 
	bool   vsync;       ///< Enable VSync?
 
	uint8 *anim_buffer; ///< Animation buffer from OpenGL back-end.
 

	
 
	uint8 GetFullscreenBpp() override { return 32; } // OpenGL is always 32 bpp.
 

	
 
	void Paint() override;
 
	void PaintThread() override {}
 

	
 
	bool AllocateBackingStore(int w, int h, bool force = false) override;
 
	void *GetVideoPointer() override;
 
	void ReleaseVideoPointer() override;
 
	void PaletteChanged(HWND hWnd) override {}
 

	
 
	const char *AllocateContext();
 
	void DestroyContext();
 
};
 

	
 
/** The factory for Windows' OpenGL video driver. */
 
class FVideoDriver_Win32OpenGL : public DriverFactoryBase {
0 comments (0 inline, 0 general)