Changeset - r24851:a41e925d9dc7
[Not reviewed]
master
0 11 0
Patric Stout - 3 years ago 2021-02-20 10:08:20
truebrain@openttd.org
Codechange: be consistent in naming the paint function Paint()

Also move this function to be a class member. This to allow
further deduplicating of code in a later commit.
11 files changed with 58 insertions and 23 deletions:
0 comments (0 inline, 0 general)
src/video/allegro_v.cpp
Show inline comments
 
@@ -35,49 +35,49 @@
 
/* Allegro replaces SEGV/ABRT signals meaning that the debugger will never
 
 * be triggered, so rereplace the signals and make the debugger useful. */
 
#include <signal.h>
 
#endif
 

	
 
static FVideoDriver_Allegro iFVideoDriver_Allegro;
 

	
 
static BITMAP *_allegro_screen;
 

	
 
#define MAX_DIRTY_RECTS 100
 
static PointDimension _dirty_rects[MAX_DIRTY_RECTS];
 
static int _num_dirty_rects;
 

	
 
void VideoDriver_Allegro::MakeDirty(int left, int top, int width, int height)
 
{
 
	if (_num_dirty_rects < MAX_DIRTY_RECTS) {
 
		_dirty_rects[_num_dirty_rects].x = left;
 
		_dirty_rects[_num_dirty_rects].y = top;
 
		_dirty_rects[_num_dirty_rects].width = width;
 
		_dirty_rects[_num_dirty_rects].height = height;
 
	}
 
	_num_dirty_rects++;
 
}
 

	
 
static void DrawSurfaceToScreen()
 
void VideoDriver_Allegro::Paint()
 
{
 
	PerformanceMeasurer framerate(PFE_VIDEO);
 

	
 
	int n = _num_dirty_rects;
 
	if (n == 0) return;
 

	
 
	_num_dirty_rects = 0;
 
	if (n > MAX_DIRTY_RECTS) {
 
		blit(_allegro_screen, screen, 0, 0, 0, 0, _allegro_screen->w, _allegro_screen->h);
 
		return;
 
	}
 

	
 
	for (int i = 0; i < n; i++) {
 
		blit(_allegro_screen, screen, _dirty_rects[i].x, _dirty_rects[i].y, _dirty_rects[i].x, _dirty_rects[i].y, _dirty_rects[i].width, _dirty_rects[i].height);
 
	}
 
}
 

	
 

	
 
static void UpdatePalette(uint start, uint count)
 
{
 
	static PALETTE pal;
 

	
 
	uint end = start + count;
 
	for (uint i = start; i != end; i++) {
 
@@ -503,49 +503,49 @@ void VideoDriver_Allegro::MainLoop()
 

	
 
		if (cur_ticks >= next_game_tick || (_fast_forward && !_pause_mode)) {
 
			if (_fast_forward && !_pause_mode) {
 
				next_game_tick = cur_ticks + this->GetGameInterval();
 
			} else {
 
				next_game_tick += this->GetGameInterval();
 
				/* 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;
 
			}
 

	
 
			GameLoop();
 
		}
 

	
 
		/* 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. */
 
			if (next_draw_tick < cur_ticks - ALLOWED_DRIFT * this->GetDrawInterval()) next_draw_tick = cur_ticks;
 

	
 
			this->InputLoop();
 
			::InputLoop();
 
			UpdateWindows();
 
			CheckPaletteAnim();
 

	
 
			DrawSurfaceToScreen();
 
			this->Paint();
 
		}
 

	
 
		/* If we are not in fast-forward, create some time between calls to ease up CPU usage. */
 
		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) {
 
				std::this_thread::sleep_for(next_tick - now);
 
			}
 
		}
 
	}
 
}
 

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

	
 
bool VideoDriver_Allegro::ToggleFullscreen(bool fullscreen)
 
{
 
	_fullscreen = fullscreen;
 
	GetVideoModes(); // get the list of available video modes
src/video/allegro_v.h
Show inline comments
 
@@ -14,34 +14,35 @@
 

	
 
/** The allegro video driver. */
 
class VideoDriver_Allegro : public VideoDriver {
 
public:
 
	const char *Start(const StringList &param) override;
 

	
 
	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;
 

	
 
	bool ClaimMousePointer() override;
 

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

	
 
protected:
 
	void InputLoop() override;
 
	void Paint() override;
 
};
 

	
 
/** Factory for the allegro video driver. */
 
class FVideoDriver_Allegro : public DriverFactoryBase {
 
public:
 
	FVideoDriver_Allegro() : DriverFactoryBase(Driver::DT_VIDEO, 4, "allegro", "Allegro Video Driver") {}
 
	Driver *CreateInstance() const override { return new VideoDriver_Allegro(); }
 
};
 

	
 
#endif /* VIDEO_ALLEGRO_H */
src/video/cocoa/cocoa_v.h
Show inline comments
 
@@ -54,49 +54,49 @@ public:
 
	const char *Start(const StringList &param) override;
 
	void Stop() override;
 
	void MainLoop() override;
 

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

	
 
	bool ChangeResolution(int w, int h) override;
 
	bool ToggleFullscreen(bool fullscreen) override;
 

	
 
	void EditBoxLostFocus() override;
 

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

	
 
	/* --- The following methods should be private, but can't be due to Obj-C limitations. --- */
 

	
 
	void GameLoop();
 

	
 
	void AllocateBackingStore();
 

	
 
protected:
 
	Dimension GetScreenSize() const override;
 
	float GetDPIScale() override;
 
	void InputLoop() override;
 
	void Paint() override;
 

	
 
private:
 
	bool PollEvent();
 

	
 
	bool IsFullscreen();
 
	void GameSizeChanged();
 

	
 
	void UpdateVideoModes();
 

	
 
	bool MakeWindow(int width, int height);
 

	
 
	void UpdatePalette(uint first_color, uint num_colors);
 
	void CheckPaletteAnim();
 

	
 
	void Draw(bool force_update = false);
 
	void BlitIndexedToView32(int left, int top, int right, int bottom);
 
};
 

	
 
class FVideoDriver_Cocoa : public DriverFactoryBase {
 
public:
 
	FVideoDriver_Cocoa() : DriverFactoryBase(Driver::DT_VIDEO, 10, "cocoa", "Cocoa Video Driver") {}
 
	Driver *CreateInstance() const override { return new VideoDriver_Cocoa(); }
 
};
 

	
 
#endif /* VIDEO_COCOA_H */
src/video/cocoa/cocoa_v.mm
Show inline comments
 
@@ -444,88 +444,87 @@ bool VideoDriver_Cocoa::MakeWindow(int w
 
/**
 
 * This function copies 8bpp pixels to the screen buffer in 32bpp windowed mode.
 
 *
 
 * @param left The x coord for the left edge of the box to blit.
 
 * @param top The y coord for the top edge of the box to blit.
 
 * @param right The x coord for the right edge of the box to blit.
 
 * @param bottom The y coord for the bottom edge of the box to blit.
 
 */
 
void VideoDriver_Cocoa::BlitIndexedToView32(int left, int top, int right, int bottom)
 
{
 
	const uint32 *pal   = this->palette;
 
	const uint8  *src   = (uint8*)this->pixel_buffer;
 
	uint32       *dst   = (uint32*)this->window_buffer;
 
	uint          width = this->window_width;
 
	uint          pitch = this->window_pitch;
 

	
 
	for (int y = top; y < bottom; y++) {
 
		for (int x = left; x < right; x++) {
 
			dst[y * pitch + x] = pal[src[y * width + x]];
 
		}
 
	}
 
}
 

	
 
/**
 
 * Draw window.
 
 * Paint window.
 
 * @param force_update Whether to redraw unconditionally
 
 */
 
void VideoDriver_Cocoa::Draw(bool force_update)
 
void VideoDriver_Cocoa::Paint()
 
{
 
	PerformanceMeasurer framerate(PFE_VIDEO);
 

	
 
	/* Check if we need to do anything */
 
	if (this->num_dirty_rects == 0 || [ this->window isMiniaturized ]) return;
 

	
 
	if (this->num_dirty_rects >= lengthof(this->dirty_rects)) {
 
		this->num_dirty_rects = 1;
 
		this->dirty_rects[0].left = 0;
 
		this->dirty_rects[0].top = 0;
 
		this->dirty_rects[0].right = this->window_width;
 
		this->dirty_rects[0].bottom = this->window_height;
 
	}
 

	
 
	/* Build the region of dirty rectangles */
 
	for (uint i = 0; i < this->num_dirty_rects; i++) {
 
		/* We only need to blit in indexed mode since in 32bpp mode the game draws directly to the image. */
 
		if (this->buffer_depth == 8) {
 
			BlitIndexedToView32(
 
				this->dirty_rects[i].left,
 
				this->dirty_rects[i].top,
 
				this->dirty_rects[i].right,
 
				this->dirty_rects[i].bottom
 
			);
 
		}
 

	
 
		NSRect dirtyrect;
 
		dirtyrect.origin.x = this->dirty_rects[i].left;
 
		dirtyrect.origin.y = this->window_height - this->dirty_rects[i].bottom;
 
		dirtyrect.size.width = this->dirty_rects[i].right - this->dirty_rects[i].left;
 
		dirtyrect.size.height = this->dirty_rects[i].bottom - this->dirty_rects[i].top;
 

	
 
		/* Normally drawRect will be automatically called by Mac OS X during next update cycle,
 
		 * and then blitting will occur. If force_update is true, it will be done right now. */
 
		 * and then blitting will occur. */
 
		[ this->cocoaview setNeedsDisplayInRect:[ this->cocoaview getVirtualRect:dirtyrect ] ];
 
		if (force_update) [ this->cocoaview displayIfNeeded ];
 
	}
 

	
 
	this->num_dirty_rects = 0;
 
}
 

	
 
/** Update the palette. */
 
void VideoDriver_Cocoa::UpdatePalette(uint first_color, uint num_colors)
 
{
 
	if (this->buffer_depth != 8) return;
 

	
 
	for (uint i = first_color; i < first_color + num_colors; i++) {
 
		uint32 clr = 0xff000000;
 
		clr |= (uint32)_cur_palette.palette[i].r << 16;
 
		clr |= (uint32)_cur_palette.palette[i].g << 8;
 
		clr |= (uint32)_cur_palette.palette[i].b;
 
		this->palette[i] = clr;
 
	}
 

	
 
	this->num_dirty_rects = lengthof(this->dirty_rects);
 
}
 

	
 
/** Clear buffer to opaque black. */
 
static void ClearWindowBuffer(uint32 *buffer, uint32 pitch, uint32 height)
 
{
 
@@ -687,49 +686,49 @@ void VideoDriver_Cocoa::GameLoop()
 

	
 
			if (cur_ticks >= next_game_tick || (_fast_forward && !_pause_mode)) {
 
				if (_fast_forward && !_pause_mode) {
 
					next_game_tick = cur_ticks + this->GetGameInterval();
 
				} else {
 
					next_game_tick += this->GetGameInterval();
 
					/* 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;
 
				}
 

	
 
				::GameLoop();
 
			}
 

	
 
			/* 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. */
 
				if (next_draw_tick < cur_ticks - ALLOWED_DRIFT * this->GetDrawInterval()) next_draw_tick = cur_ticks;
 

	
 
				this->InputLoop();
 
				::InputLoop();
 
				UpdateWindows();
 
				this->CheckPaletteAnim();
 

	
 
				this->Draw();
 
				this->Paint();
 
			}
 

	
 
			/* If we are not in fast-forward, create some time between calls to ease up CPU usage. */
 
			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) {
 
					std::this_thread::sleep_for(next_tick - now);
 
				}
 
			}
 
		}
 
	}
 
}
 

	
 

	
 
@implementation OTTD_QuartzView
 

	
 
- (instancetype)initWithFrame:(NSRect)frameRect andDriver:(VideoDriver_Cocoa *)drv
 
{
 
	if (self = [ super initWithFrame:frameRect ]) {
 
		self->driver = drv;
 

	
src/video/sdl2_v.cpp
Show inline comments
 
@@ -103,101 +103,106 @@ static void MakePalette()
 
		 * this favors colors further up into the palette. Also
 
		 * note that if two colors from the same animation
 
		 * sequence are approximated using the same color, that
 
		 * animation will stop working.
 
		 *
 
		 * Since changing the system palette causes the colours
 
		 * to change right away, and allocations might
 
		 * drastically change, we can't use this for animation,
 
		 * since that could cause weird coloring between the
 
		 * palette change and the blitting below, so we only set
 
		 * the real palette during initialisation.
 
		 */
 
		SDL_SetSurfacePalette(_sdl_real_surface, _sdl_palette);
 
	}
 
}
 

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

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

	
 
static void Paint()
 
void VideoDriver_SDL::Paint()
 
{
 
	PerformanceMeasurer framerate(PFE_VIDEO);
 

	
 
	if (IsEmptyRect(_dirty_rect) && _cur_palette.count_dirty == 0) return;
 

	
 
	if (_cur_palette.count_dirty != 0) {
 
		Blitter *blitter = BlitterFactory::GetCurrentBlitter();
 

	
 
		switch (blitter->UsePaletteAnimation()) {
 
			case Blitter::PALETTE_ANIMATION_VIDEO_BACKEND:
 
				UpdatePalette();
 
				break;
 

	
 
			case Blitter::PALETTE_ANIMATION_BLITTER:
 
				blitter->PaletteAnimate(_local_palette);
 
				break;
 

	
 
			case Blitter::PALETTE_ANIMATION_NONE:
 
				break;
 

	
 
			default:
 
				NOT_REACHED();
 
		}
 
		_cur_palette.count_dirty = 0;
 
	}
 

	
 
	SDL_Rect r = { _dirty_rect.left, _dirty_rect.top, _dirty_rect.right - _dirty_rect.left, _dirty_rect.bottom - _dirty_rect.top };
 

	
 
	if (_sdl_surface != _sdl_real_surface) {
 
		SDL_BlitSurface(_sdl_surface, &r, _sdl_real_surface, &r);
 
	}
 
	SDL_UpdateWindowSurfaceRects(_sdl_window, &r, 1);
 

	
 
	MemSetT(&_dirty_rect, 0);
 
}
 

	
 
static void PaintThread()
 
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 */
 
		Paint();
 
		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 }
 
};
 

	
 
static void FindResolutions()
 
{
 
	_resolutions.clear();
 

	
 
	for (int i = 0; i < SDL_GetNumDisplayModes(0); i++) {
 
		SDL_DisplayMode mode;
 
		SDL_GetDisplayMode(0, i, &mode);
 

	
 
		if (mode.w < 640 || mode.h < 480) continue;
 
		if (std::find(_resolutions.begin(), _resolutions.end(), Dimension(mode.w, mode.h)) != _resolutions.end()) continue;
 
@@ -799,90 +804,90 @@ void VideoDriver_SDL::LoopOnce()
 
			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. */
 
		this->UnlockVideoBuffer();
 
		GameLoop();
 
		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. */
 
		if (next_draw_tick < cur_ticks - ALLOWED_DRIFT * this->GetDrawInterval()) next_draw_tick = cur_ticks;
 

	
 
		this->InputLoop();
 
		::InputLoop();
 
		UpdateWindows();
 
		this->CheckPaletteAnim();
 

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

	
 
/* Emscripten is running an event-based mainloop; there is already some
 
 * downtime between each iteration, so no need to sleep. */
 
#ifndef __EMSCRIPTEN__
 
	/* If we are not in fast-forward, create some time between calls to ease up CPU usage. */
 
	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) {
 
			this->UnlockVideoBuffer();
 
			std::this_thread::sleep_for(next_tick - now);
 
			this->LockVideoBuffer();
 
		}
 
	}
 
#endif
 
}
 

	
 
void VideoDriver_SDL::MainLoop()
 
{
 
	cur_ticks = std::chrono::steady_clock::now();
 
	last_realtime_tick = cur_ticks;
 
	next_game_tick = cur_ticks;
 

	
 
	this->CheckPaletteAnim();
 

	
 
	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);
 
			_draw_signal = new std::condition_variable_any();
 
			_draw_continue = true;
 

	
 
			_draw_threaded = StartNewThread(&draw_thread, "ottd:draw-sdl", &PaintThread);
 
			_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) {
 
				draw_lock.unlock();
 
				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, "SDL2: using %sthreads", _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();
src/video/sdl2_v.h
Show inline comments
 
@@ -25,61 +25,65 @@ public:
 

	
 
	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:
 
	Dimension GetScreenSize() const override;
 
	void InputLoop() override;
 
	bool LockVideoBuffer() override;
 
	void UnlockVideoBuffer() override;
 
	void Paint() override;
 
	void PaintThread() override;
 

	
 
private:
 
	int PollEvent();
 
	void LoopOnce();
 
	void MainLoopCleanup();
 
	bool CreateMainSurface(uint w, uint h, bool resize);
 
	bool CreateMainWindow(uint w, uint h);
 
	void CheckPaletteAnim();
 

	
 
#ifdef __EMSCRIPTEN__
 
	/* Convert a constant pointer back to a non-constant pointer to a member function. */
 
	static void EmscriptenLoop(void *self) { ((VideoDriver_SDL *)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;
 

	
 
	std::chrono::steady_clock::time_point cur_ticks;
 
	std::chrono::steady_clock::time_point last_realtime_tick;
 
	std::chrono::steady_clock::time_point next_game_tick;
 
	std::chrono::steady_clock::time_point next_draw_tick;
 

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

	
 
	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/sdl_v.cpp
Show inline comments
 
@@ -126,91 +126,96 @@ static void InitPalette()
 
static void CheckPaletteAnim()
 
{
 
	if (_cur_palette.count_dirty != 0) {
 
		Blitter *blitter = BlitterFactory::GetCurrentBlitter();
 

	
 
		switch (blitter->UsePaletteAnimation()) {
 
			case Blitter::PALETTE_ANIMATION_VIDEO_BACKEND:
 
				UpdatePalette();
 
				break;
 

	
 
			case Blitter::PALETTE_ANIMATION_BLITTER:
 
				blitter->PaletteAnimate(_local_palette);
 
				break;
 

	
 
			case Blitter::PALETTE_ANIMATION_NONE:
 
				break;
 

	
 
			default:
 
				NOT_REACHED();
 
		}
 
		_cur_palette.count_dirty = 0;
 
	}
 
}
 

	
 
static void DrawSurfaceToScreen()
 
void VideoDriver_SDL::Paint()
 
{
 
	PerformanceMeasurer framerate(PFE_VIDEO);
 

	
 
	int n = _num_dirty_rects;
 
	if (n == 0) return;
 

	
 
	_num_dirty_rects = 0;
 

	
 
	if (n > MAX_DIRTY_RECTS) {
 
		if (_sdl_surface != _sdl_realscreen) {
 
			SDL_BlitSurface(_sdl_surface, nullptr, _sdl_realscreen, nullptr);
 
		}
 

	
 
		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);
 
	}
 
}
 

	
 
static void DrawSurfaceToScreenThread()
 
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) {
 
		CheckPaletteAnim();
 
		/* Then just draw and wait till we stop */
 
		DrawSurfaceToScreen();
 
		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}
 
};
 

	
 
static void GetVideoModes()
 
{
 
	SDL_Rect **modes = SDL_ListModes(nullptr, SDL_SWSURFACE | SDL_FULLSCREEN);
 
	if (modes == nullptr) usererror("sdl: no modes available");
 

	
 
	_resolutions.clear();
 

	
 
	_all_modes = (SDL_ListModes(nullptr, SDL_SWSURFACE | (_fullscreen ? SDL_FULLSCREEN : 0)) == (void*)-1);
 
	if (modes == (void*)-1) {
 
		for (uint i = 0; i < lengthof(_default_resolutions); i++) {
 
@@ -696,49 +701,49 @@ void VideoDriver_SDL::InputLoop()
 
	if (old_ctrl_pressed != _ctrl_pressed) HandleCtrlChanged();
 
}
 

	
 
void VideoDriver_SDL::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;
 

	
 
	CheckPaletteAnim();
 

	
 
	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", &DrawSurfaceToScreenThread);
 
			_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 (;;) {
 
		InteractiveRandom(); // randomness
 

	
 
		while (PollEvent() == -1) {}
 
		if (_exit_game) break;
 

	
 
@@ -761,49 +766,49 @@ void VideoDriver_SDL::MainLoop()
 
			}
 

	
 
			/* The gameloop is the part that can run asynchronously. The rest
 
			 * except sleeping can't. */
 
			this->UnlockVideoBuffer();
 
			GameLoop();
 
			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. */
 
			if (next_draw_tick < cur_ticks - ALLOWED_DRIFT * this->GetDrawInterval()) next_draw_tick = cur_ticks;
 

	
 
			this->InputLoop();
 
			::InputLoop();
 
			UpdateWindows();
 
			_local_palette = _cur_palette;
 

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

	
 
		/* If we are not in fast-forward, create some time between calls to ease up CPU usage. */
 
		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) {
 
				this->UnlockVideoBuffer();
 
				std::this_thread::sleep_for(next_tick - now);
 
				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 (this->draw_lock.owns_lock()) this->draw_lock.unlock();
 
		this->draw_lock.release();
src/video/sdl_v.h
Show inline comments
 
@@ -20,41 +20,45 @@ 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;
 

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

	
 
	int PollEvent();
 
	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.hpp
Show inline comments
 
@@ -153,36 +153,46 @@ protected:
 
			Dimension res = this->GetScreenSize();
 
			_cur_resolution.width  = ClampU(res.width  * 3 / 4, DEFAULT_WINDOW_WIDTH, UINT16_MAX / 2);
 
			_cur_resolution.height = ClampU(res.height * 3 / 4, DEFAULT_WINDOW_HEIGHT, UINT16_MAX / 2);
 
		}
 
	}
 

	
 
	/**
 
	 * 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() {}
 

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

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

	
 
	std::chrono::steady_clock::duration GetGameInterval()
 
	{
 
		return std::chrono::milliseconds(MILLISECONDS_PER_TICK);
 
	}
 

	
 
	std::chrono::steady_clock::duration GetDrawInterval()
 
	{
 
		return std::chrono::microseconds(1000000 / _settings_client.gui.refresh_rate);
 
	}
 
};
 

	
 
#endif /* VIDEO_VIDEO_DRIVER_HPP */
src/video/win32_v.cpp
Show inline comments
 
@@ -311,49 +311,49 @@ bool VideoDriver_Win32::MakeWindow(bool 
 
		h = r.bottom - r.top;
 

	
 
		if (_wnd.main_wnd != nullptr) {
 
			if (!_window_maximize) SetWindowPos(_wnd.main_wnd, 0, 0, 0, w, h, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOMOVE);
 
		} else {
 
			int x = (GetSystemMetrics(SM_CXSCREEN) - w) / 2;
 
			int y = (GetSystemMetrics(SM_CYSCREEN) - h) / 2;
 

	
 
			char window_title[64];
 
			seprintf(window_title, lastof(window_title), "OpenTTD %s", _openttd_revision);
 

	
 
			_wnd.main_wnd = CreateWindow(_T("OTTD"), MB_TO_WIDE(window_title), style, x, y, w, h, 0, 0, GetModuleHandle(nullptr), 0);
 
			if (_wnd.main_wnd == nullptr) usererror("CreateWindow failed");
 
			ShowWindow(_wnd.main_wnd, showstyle);
 
		}
 
	}
 

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

	
 
	GameSizeChanged(); // invalidate all windows, force redraw
 
	return true; // the request succeeded
 
}
 

	
 
/** Do palette animation and blit to the window. */
 
static void PaintWindow()
 
void VideoDriver_Win32::Paint()
 
{
 
	PerformanceMeasurer framerate(PFE_VIDEO);
 

	
 
	if (IsEmptyRect(_dirty_rect)) return;
 

	
 
	/* Convert update region from logical to device coordinates. */
 
	POINT pt = {0, 0};
 
	ClientToScreen(_wnd.main_wnd, &pt);
 

	
 
	RECT r = { _dirty_rect.left, _dirty_rect.top, _dirty_rect.right, _dirty_rect.bottom };
 
	OffsetRect(&r, pt.x, pt.y);
 

	
 
	/* Create a device context that is clipped to the region we need to draw.
 
	 * GetDCEx 'consumes' the update region, so we may not destroy it ourself. */
 
	HRGN rgn = CreateRectRgnIndirect(&r);
 
	HDC dc = GetDCEx(_wnd.main_wnd, rgn, DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN | DCX_INTERSECTRGN);
 

	
 
	HDC dc2 = CreateCompatibleDC(dc);
 
	HBITMAP old_bmp = (HBITMAP)SelectObject(dc2, _wnd.dib_sect);
 
	HPALETTE old_palette = SelectPalette(dc, _wnd.gdi_palette, FALSE);
 

	
 
	if (_cur_palette.count_dirty != 0) {
 
		Blitter *blitter = BlitterFactory::GetCurrentBlitter();
 

	
 
@@ -364,67 +364,72 @@ static void PaintWindow()
 

	
 
			case Blitter::PALETTE_ANIMATION_BLITTER:
 
				blitter->PaletteAnimate(_local_palette);
 
				break;
 

	
 
			case Blitter::PALETTE_ANIMATION_NONE:
 
				break;
 

	
 
			default:
 
				NOT_REACHED();
 
		}
 
		_cur_palette.count_dirty = 0;
 
	}
 

	
 
	BitBlt(dc, 0, 0, _wnd.width, _wnd.height, dc2, 0, 0, SRCCOPY);
 
	SelectPalette(dc, old_palette, TRUE);
 
	SelectObject(dc2, old_bmp);
 
	DeleteDC(dc2);
 

	
 
	ReleaseDC(_wnd.main_wnd, dc);
 

	
 
	MemSetT(&_dirty_rect, 0);
 
}
 

	
 
static void PaintWindowThread()
 
void VideoDriver_Win32::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) {
 
		PaintWindow();
 
		this->Paint();
 

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

	
 
		_draw_signal->wait(*_draw_mutex);
 
	}
 
}
 

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

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

	
 
	char input[2] = {(char)charcode, 0};
 
	int input_len = 1;
 

	
 
	if (prev_char != 0) {
 
		/* We stored a lead byte previously, combine it with this byte. */
 
		input[0] = prev_char;
 
		input[1] = (char)charcode;
 
		input_len = 2;
 
	} else if (IsDBCSLeadByte(charcode)) {
 
		/* We got a lead byte, store and exit. */
 
		prev_char = charcode;
 
		return 0;
 
	}
 
	prev_char = 0;
 

	
 
	wchar_t w[2]; // Can get up to two code points as a result.
 
	int len = MultiByteToWideChar(CP_ACP, 0, input, input_len, w, 2);
 
	switch (len) {
 
@@ -1155,49 +1160,49 @@ void VideoDriver_Win32::InputLoop()
 
void VideoDriver_Win32::MainLoop()
 
{
 
	MSG mesg;
 
	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;
 

	
 
	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) {
 
			this->draw_lock = std::unique_lock<std::recursive_mutex>(*_draw_mutex);
 

	
 
			_draw_continue = true;
 
			_draw_threaded = StartNewThread(&draw_thread, "ottd:draw-win32", &PaintWindowThread);
 
			_draw_threaded = StartNewThread(&draw_thread, "ottd:draw-win32", &VideoDriver_Win32::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 {
 
				DEBUG(driver, 1, "Threaded drawing enabled");
 
				/* Wait till the draw thread has started itself. */
 
				_draw_signal->wait(*_draw_mutex);
 
			}
 
		}
 
	}
 

	
 
	_wnd.running = true;
 

	
 
	CheckPaletteAnim();
 
	for (;;) {
 
		InteractiveRandom(); // randomness
 

	
 
		while (PeekMessage(&mesg, nullptr, 0, 0, PM_REMOVE)) {
 
@@ -1233,49 +1238,49 @@ void VideoDriver_Win32::MainLoop()
 
			this->UnlockVideoBuffer();
 
			GameLoop();
 
			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. */
 
			if (next_draw_tick < cur_ticks - ALLOWED_DRIFT * this->GetDrawInterval()) next_draw_tick = cur_ticks;
 

	
 
			if (_force_full_redraw) MarkWholeScreenDirty();
 

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

	
 
			this->InputLoop();
 
			::InputLoop();
 
			UpdateWindows();
 
			CheckPaletteAnim();
 

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

	
 
		/* If we are not in fast-forward, create some time between calls to ease up CPU usage. */
 
		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) {
 
				/* Flush GDI buffer to ensure we don't conflict with the drawing thread. */
 
				GdiFlush();
 

	
 
				this->UnlockVideoBuffer();
 
				std::this_thread::sleep_for(next_tick - now);
 
				this->LockVideoBuffer();
 
			}
 
		}
 
	}
 

	
 
	if (_draw_threaded) {
 
		_draw_continue = false;
 
		/* Sending signal if there is no thread blocked
 
		 * is very valid and results in noop */
src/video/win32_v.h
Show inline comments
 
@@ -26,41 +26,43 @@ public:
 
	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 EditBoxLostFocus() override;
 

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

	
 
	bool MakeWindow(bool full_screen);
 

	
 
protected:
 
	Dimension GetScreenSize() const override;
 
	float GetDPIScale() override;
 
	void InputLoop() override;
 
	bool LockVideoBuffer() override;
 
	void UnlockVideoBuffer() override;
 
	void Paint() override;
 
	void PaintThread() 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") {}
 
	Driver *CreateInstance() const override { return new VideoDriver_Win32(); }
 
};
 

	
 
#endif /* VIDEO_WIN32_H */
0 comments (0 inline, 0 general)