Merge branch 'master' into jgrpp

This commit is contained in:
Jonathan G Rennison
2021-04-05 17:50:39 +01:00
164 changed files with 3493 additions and 2443 deletions

View File

@@ -236,6 +236,16 @@ bool VideoDriver_Allegro::ClaimMousePointer()
return true;
}
std::vector<int> VideoDriver_Allegro::GetListOfMonitorRefreshRates()
{
std::vector<int> rates = {};
int refresh_rate = get_refresh_rate();
if (refresh_rate != 0) rates.push_back(refresh_rate);
return rates;
}
struct AllegroVkMapping {
uint16 vk_from;
byte vk_count;
@@ -412,7 +422,7 @@ bool VideoDriver_Allegro::PollEvent()
*/
int _allegro_instance_count = 0;
const char *VideoDriver_Allegro::Start(const StringList &parm)
const char *VideoDriver_Allegro::Start(const StringList &param)
{
if (_allegro_instance_count == 0 && install_allegro(SYSTEM_AUTODETECT, &errno, nullptr)) {
DEBUG(driver, 0, "allegro: install_allegro failed '%s'", allegro_error);
@@ -440,6 +450,8 @@ const char *VideoDriver_Allegro::Start(const StringList &parm)
MarkWholeScreenDirty();
set_close_button_callback(HandleExitGameRequest);
this->is_game_threaded = !GetDriverParamBool(param, "no_threads") && !GetDriverParamBool(param, "no_thread");
return nullptr;
}
@@ -477,14 +489,16 @@ void VideoDriver_Allegro::InputLoop()
void VideoDriver_Allegro::MainLoop()
{
for (;;) {
if (_exit_game) return;
this->StartGameThread();
if (this->Tick()) {
this->Paint();
}
for (;;) {
if (_exit_game) break;
this->Tick();
this->SleepTillNextTick();
}
this->StopGameThread();
}
bool VideoDriver_Allegro::ChangeResolution(int w, int h)

View File

@@ -31,6 +31,8 @@ public:
bool ClaimMousePointer() override;
std::vector<int> GetListOfMonitorRefreshRates() override;
const char *GetName() const override { return "allegro"; }
protected:

View File

@@ -33,6 +33,8 @@ public:
void ClearSystemSprites() override;
void PopulateSystemSprites() override;
bool HasAnimBuffer() override { return true; }
uint8 *GetAnimBuffer() override { return this->anim_buffer; }
@@ -54,6 +56,9 @@ class FVideoDriver_CocoaOpenGL : public DriverFactoryBase {
public:
FVideoDriver_CocoaOpenGL() : DriverFactoryBase(Driver::DT_VIDEO, 9, "cocoa-opengl", "Cocoa OpenGL Video Driver") {}
Driver *CreateInstance() const override { return new VideoDriver_CocoaOpenGL(); }
protected:
bool UsesHardwareAcceleration() const override { return true; }
};
#endif /* VIDEO_COCOA_OGL_H */

View File

@@ -214,6 +214,8 @@ const char *VideoDriver_CocoaOpenGL::Start(const StringList &param)
this->UpdateVideoModes();
MarkWholeScreenDirty();
this->is_game_threaded = !GetDriverParamBool(param, "no_threads") && !GetDriverParamBool(param, "no_thread");
return nullptr;
}
@@ -227,6 +229,11 @@ void VideoDriver_CocoaOpenGL::Stop()
CGLReleaseContext(this->gl_context);
}
void VideoDriver_CocoaOpenGL::PopulateSystemSprites()
{
OpenGLBackend::Get()->PopulateCursorCache();
}
void VideoDriver_CocoaOpenGL::ClearSystemSprites()
{
CGLSetCurrentContext(this->gl_context);
@@ -265,7 +272,7 @@ void VideoDriver_CocoaOpenGL::AllocateBackingStore(bool force)
CGLSetCurrentContext(this->gl_context);
NSRect frame = [ this->cocoaview getRealRect:[ this->cocoaview frame ] ];
OpenGLBackend::Get()->Resize(frame.size.width, frame.size.height, force);
_screen.dst_ptr = this->GetVideoPointer();
if (this->buffer_locked) _screen.dst_ptr = this->GetVideoPointer();
this->dirty_rect = {};
/* Redraw screen */

View File

@@ -47,6 +47,8 @@ public:
void EditBoxLostFocus() override;
std::vector<int> GetListOfMonitorRefreshRates() override;
/* --- The following methods should be private, but can't be due to Obj-C limitations. --- */
void MainLoopReal();
@@ -122,7 +124,7 @@ protected:
class FVideoDriver_CocoaQuartz : public DriverFactoryBase {
public:
FVideoDriver_CocoaQuartz() : DriverFactoryBase(Driver::DT_VIDEO, 10, "cocoa", "Cocoa Video Driver") {}
FVideoDriver_CocoaQuartz() : DriverFactoryBase(Driver::DT_VIDEO, 8, "cocoa", "Cocoa Video Driver") {}
Driver *CreateInstance() const override { return new VideoDriver_CocoaQuartz(); }
};

View File

@@ -43,6 +43,7 @@
#import <sys/param.h> /* for MAXPATHLEN */
#import <sys/time.h> /* gettimeofday */
#include <array>
/**
* Important notice regarding all modifications!!!!!!!
@@ -201,6 +202,7 @@ bool VideoDriver_Cocoa::ToggleFullscreen(bool full_screen)
[ NSMenu setMenuBarVisible:!full_screen ];
this->UpdateVideoModes();
this->InvalidateGameOptionsWindow();
return true;
}
@@ -213,7 +215,7 @@ bool VideoDriver_Cocoa::ToggleFullscreen(bool full_screen)
*/
bool VideoDriver_Cocoa::AfterBlitterChange()
{
this->ChangeResolution(_cur_resolution.width, _cur_resolution.height);
this->AllocateBackingStore(true);
return true;
}
@@ -224,7 +226,31 @@ void VideoDriver_Cocoa::EditBoxLostFocus()
{
[ [ this->cocoaview inputContext ] discardMarkedText ];
/* Clear any marked string from the current edit box. */
HandleTextInput(NULL, true);
HandleTextInput(nullptr, true);
}
/**
* Get refresh rates of all connected monitors.
*/
std::vector<int> VideoDriver_Cocoa::GetListOfMonitorRefreshRates()
{
std::vector<int> rates{};
if (MacOSVersionIsAtLeast(10, 6, 0)) {
std::array<CGDirectDisplayID, 16> displays;
uint32_t count = 0;
CGGetActiveDisplayList(displays.size(), displays.data(), &count);
for (uint32_t i = 0; i < count; i++) {
CGDisplayModeRef mode = CGDisplayCopyDisplayMode(displays[i]);
int rate = (int)CGDisplayModeGetRefreshRate(mode);
if (rate > 0) rates.push_back(rate);
CGDisplayModeRelease(mode);
}
}
return rates;
}
/**
@@ -436,6 +462,8 @@ void VideoDriver_Cocoa::InputLoop()
/** Main game loop. */
void VideoDriver_Cocoa::MainLoopReal()
{
this->StartGameThread();
for (;;) {
@autoreleasepool {
if (_exit_game) {
@@ -444,12 +472,12 @@ void VideoDriver_Cocoa::MainLoopReal()
break;
}
if (this->Tick()) {
this->Paint();
}
this->Tick();
this->SleepTillNextTick();
}
}
this->StopGameThread();
}
@@ -561,6 +589,8 @@ const char *VideoDriver_CocoaQuartz::Start(const StringList &param)
this->GameSizeChanged();
this->UpdateVideoModes();
this->is_game_threaded = !GetDriverParamBool(param, "no_threads") && !GetDriverParamBool(param, "no_thread");
return nullptr;
}

View File

@@ -700,9 +700,9 @@ void CocoaDialog(const char *title, const char *message, const char *buttonLabel
if (!EditBoxInGlobalFocus() || IsInsideMM(pressed_key & ~WKC_SPECIAL_KEYS, WKC_F1, WKC_PAUSE + 1)) {
HandleKeypress(pressed_key, unicode);
}
DEBUG(driver, 2, "cocoa_v: QZ_KeyEvent: %x (%x), down, mapping: %x", keycode, unicode, pressed_key);
DEBUG(driver, 3, "cocoa_v: QZ_KeyEvent: %x (%x), down, mapping: %x", keycode, unicode, pressed_key);
} else {
DEBUG(driver, 2, "cocoa_v: QZ_KeyEvent: %x (%x), up", keycode, unicode);
DEBUG(driver, 3, "cocoa_v: QZ_KeyEvent: %x (%x), up", keycode, unicode);
}
return interpret_keys;

View File

@@ -251,28 +251,22 @@ void VideoDriver_Dedicated::MainLoop()
/* If SwitchMode is SM_LOAD_GAME, it means that the user used the '-g' options */
if (_switch_mode != SM_LOAD_GAME) {
StartNewGameWithoutGUI(GENERATE_NEW_SEED);
SwitchToMode(_switch_mode);
_switch_mode = SM_NONE;
} else {
_switch_mode = SM_NONE;
/* First we need to test if the savegame can be loaded, else we will end up playing the
* intro game... */
if (!SafeLoad(_file_to_saveload.name, _file_to_saveload.file_op, _file_to_saveload.detail_ftype, GM_NORMAL, BASE_DIR)) {
if (SaveOrLoad(_file_to_saveload.name, _file_to_saveload.file_op, _file_to_saveload.detail_ftype, BASE_DIR) == SL_ERROR) {
/* Loading failed, pop out.. */
DEBUG(net, 0, "Loading requested map failed, aborting");
_networking = false;
return;
} else {
/* We can load this game, so go ahead */
SwitchToMode(SM_LOAD_GAME);
_switch_mode = SM_LOAD_GAME;
}
}
/* Done loading, start game! */
this->is_game_threaded = false;
if (!_networking) {
DEBUG(net, 0, "Dedicated server could not be started, aborting");
return;
}
/* Done loading, start game! */
while (!_exit_game) {
if (!_dedicated_forks) DedicatedHandleKeyInput();

View File

@@ -50,17 +50,17 @@ void VideoDriver_Null::MainLoop()
{
if (this->until_exit) {
while (!_exit_game) {
GameLoop();
GameLoopPaletteAnimations();
InputLoop();
UpdateWindows();
::GameLoop();
::GameLoopPaletteAnimations();
::InputLoop();
::UpdateWindows();
}
} else {
for (int i = 0; i < this->ticks; i++) {
GameLoop();
GameLoopPaletteAnimations();
InputLoop();
UpdateWindows();
::GameLoop();
::GameLoopPaletteAnimations();
::InputLoop();
::UpdateWindows();
}
}
}

View File

@@ -1036,7 +1036,36 @@ void OpenGLBackend::DrawMouseCursor()
_cur_dpi = &_screen;
for (uint i = 0; i < _cursor.sprite_count; ++i) {
SpriteID sprite = _cursor.sprite_seq[i].sprite;
const Sprite *spr = GetSprite(sprite, ST_NORMAL);
/* Sprites are cached by PopulateCursorCache(). */
if (this->cursor_cache.Contains(sprite)) {
const Sprite *spr = GetSprite(sprite, ST_NORMAL);
this->RenderOglSprite((OpenGLSprite *)this->cursor_cache.Get(sprite)->data, _cursor.sprite_seq[i].pal,
_cursor.pos.x + _cursor.sprite_pos[i].x + UnScaleByZoom(spr->x_offs, ZOOM_LVL_GUI),
_cursor.pos.y + _cursor.sprite_pos[i].y + UnScaleByZoom(spr->y_offs, ZOOM_LVL_GUI),
ZOOM_LVL_GUI);
}
}
}
void OpenGLBackend::PopulateCursorCache()
{
if (this->clear_cursor_cache) {
/* We have a pending cursor cache clear to do first. */
this->clear_cursor_cache = false;
this->last_sprite_pal = (PaletteID)-1;
Sprite *sp;
while ((sp = this->cursor_cache.Pop()) != nullptr) {
OpenGLSprite *sprite = (OpenGLSprite *)sp->data;
sprite->~OpenGLSprite();
free(sp);
}
}
for (uint i = 0; i < _cursor.sprite_count; ++i) {
SpriteID sprite = _cursor.sprite_seq[i].sprite;
if (!this->cursor_cache.Contains(sprite)) {
Sprite *old = this->cursor_cache.Insert(sprite, (Sprite *)GetRawSprite(sprite, ST_NORMAL, &SimpleSpriteAlloc, this));
@@ -1046,11 +1075,6 @@ void OpenGLBackend::DrawMouseCursor()
free(old);
}
}
this->RenderOglSprite((OpenGLSprite *)this->cursor_cache.Get(sprite)->data, _cursor.sprite_seq[i].pal,
_cursor.pos.x + _cursor.sprite_pos[i].x + UnScaleByZoom(spr->x_offs, ZOOM_LVL_GUI),
_cursor.pos.y + _cursor.sprite_pos[i].y + UnScaleByZoom(spr->y_offs, ZOOM_LVL_GUI),
ZOOM_LVL_GUI);
}
}
@@ -1059,14 +1083,11 @@ void OpenGLBackend::DrawMouseCursor()
*/
void OpenGLBackend::ClearCursorCache()
{
this->last_sprite_pal = (PaletteID)-1;
Sprite *sp;
while ((sp = this->cursor_cache.Pop()) != nullptr) {
OpenGLSprite *sprite = (OpenGLSprite *)sp->data;
sprite->~OpenGLSprite();
free(sp);
}
/* If the game loop is threaded, this function might be called
* from the game thread. As we can call OpenGL functions only
* on the main thread, just set a flag that is handled the next
* time we prepare the cursor cache for drawing. */
this->clear_cursor_cache = true;
}
/**

View File

@@ -63,6 +63,7 @@ private:
LRUCache<SpriteID, Sprite> cursor_cache; ///< Cache of encoded cursor sprites.
PaletteID last_sprite_pal = (PaletteID)-1; ///< Last uploaded remap palette.
bool clear_cursor_cache = false; ///< A clear of the cursor cache is pending.
OpenGLBackend();
~OpenGLBackend();
@@ -88,6 +89,7 @@ public:
void Paint();
void DrawMouseCursor();
void PopulateCursorCache();
void ClearCursorCache();
void *GetVideoBuffer();

View File

@@ -111,13 +111,7 @@ void VideoDriver_SDL_Default::Paint()
break;
case Blitter::PALETTE_ANIMATION_BLITTER: {
bool need_buf = _screen.dst_ptr == nullptr;
if (need_buf) _screen.dst_ptr = this->GetVideoPointer();
blitter->PaletteAnimate(this->local_palette);
if (need_buf) {
this->ReleaseVideoPointer();
_screen.dst_ptr = nullptr;
}
break;
}
@@ -140,22 +134,6 @@ void VideoDriver_SDL_Default::Paint()
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();

View File

@@ -21,7 +21,6 @@ protected:
bool AllocateBackingStore(int w, int h, bool force = false) override;
void *GetVideoPointer() override;
void Paint() override;
void PaintThread() override;
void ReleaseVideoPointer() override {}

View File

@@ -75,7 +75,6 @@ const char *VideoDriver_SDL_OpenGL::Start(const StringList &param)
this->ClientSizeChanged(w, h, true);
SDL_GL_SetSwapInterval(GetDriverParamBool(param, "vsync") ? 1 : 0);
this->draw_threaded = false;
return nullptr;
}
@@ -115,6 +114,11 @@ const char *VideoDriver_SDL_OpenGL::AllocateContext()
return OpenGLBackend::Create(&GetOGLProcAddressCallback);
}
void VideoDriver_SDL_OpenGL::PopulateSystemSprites()
{
OpenGLBackend::Get()->PopulateCursorCache();
}
void VideoDriver_SDL_OpenGL::ClearSystemSprites()
{
OpenGLBackend::Get()->ClearCursorCache();

View File

@@ -24,6 +24,8 @@ public:
void ClearSystemSprites() override;
void PopulateSystemSprites() override;
bool HasAnimBuffer() override { return true; }
uint8 *GetAnimBuffer() override { return this->anim_buffer; }
@@ -36,8 +38,6 @@ protected:
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.
@@ -51,4 +51,7 @@ class FVideoDriver_SDL_OpenGL : public DriverFactoryBase {
public:
FVideoDriver_SDL_OpenGL() : DriverFactoryBase(Driver::DT_VIDEO, 8, "sdl-opengl", "SDL OpenGL Video Driver") {}
/* virtual */ Driver *CreateInstance() const override { return new VideoDriver_SDL_OpenGL(); }
protected:
bool UsesHardwareAcceleration() const override { return true; }
};

View File

@@ -24,10 +24,6 @@
#include "sdl2_v.h"
#include <SDL.h>
#include <algorithm>
#include <mutex>
#if defined(__MINGW32__)
#include "../3rdparty/mingw-std-threads/mingw.mutex.h"
#endif
#ifdef __EMSCRIPTEN__
# include <emscripten.h>
# include <emscripten/html5.h>
@@ -264,11 +260,6 @@ void VideoDriver_SDL_Base::CheckPaletteAnim()
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 },
@@ -516,6 +507,17 @@ void VideoDriver_SDL_Base::EditBoxLostFocus()
HandleTextInput(nullptr, true);
}
std::vector<int> VideoDriver_SDL_Base::GetListOfMonitorRefreshRates()
{
std::vector<int> rates = {};
for (int i = 0; i < SDL_GetNumVideoDisplays(); i++) {
SDL_DisplayMode mode = {};
if (SDL_GetDisplayMode(i, 0, &mode) != 0) continue;
if (mode.refresh_rate != 0) rates.push_back(mode.refresh_rate);
}
return rates;
}
struct SDLVkMapping {
SDL_Keycode vk_from;
byte vk_count;
@@ -873,14 +875,14 @@ const char *VideoDriver_SDL_Base::Initialize()
return nullptr;
}
const char *VideoDriver_SDL_Base::Start(const StringList &parm)
const char *VideoDriver_SDL_Base::Start(const StringList &param)
{
if (BlitterFactory::GetCurrentBlitter()->GetScreenDepth() == 0) return "Only real blitters supported";
const char *error = this->Initialize();
if (error != nullptr) return error;
this->startup_display = FindStartupDisplay(GetDriverParamInt(parm, "display", -1));
this->startup_display = FindStartupDisplay(GetDriverParamInt(param, "display", -1));
if (!CreateMainSurface(_cur_resolution.width, _cur_resolution.height, false)) {
return SDL_GetError();
@@ -891,25 +893,17 @@ const char *VideoDriver_SDL_Base::Start(const StringList &parm)
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;
#if defined(WITH_FCITX)
if (_fcitx_mode) SDL_EventState(SDL_SYSWMEVENT, 1);
#endif
#ifdef __EMSCRIPTEN__
this->is_game_threaded = false;
#else
this->is_game_threaded = !GetDriverParamBool(param, "no_threads") && !GetDriverParamBool(param, "no_thread");
#endif
return nullptr;
}
@@ -966,18 +960,17 @@ void VideoDriver_SDL_Base::LoopOnce()
* 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. */
@@ -988,89 +981,27 @@ void VideoDriver_SDL_Base::LoopOnce()
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
this->StartGameThread();
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());
this->StopGameThread();
#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 */
@@ -1096,6 +1027,7 @@ bool VideoDriver_SDL_Base::ToggleFullscreen(bool fullscreen)
DEBUG(driver, 0, "SDL_SetWindowFullscreen() failed: %s", SDL_GetError());
}
this->InvalidateGameOptionsWindow();
return ret == 0;
}
@@ -1107,16 +1039,6 @@ bool VideoDriver_SDL_Base::AfterBlitterChange()
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;
@@ -1130,8 +1052,6 @@ 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);
@@ -1146,6 +1066,5 @@ void VideoDriver_SDL_Base::UnlockVideoBuffer()
_screen.dst_ptr = nullptr;
}
if (this->draw_threaded) this->draw_lock.unlock();
this->buffer_locked = false;
}

View File

@@ -36,25 +36,19 @@ public:
bool AfterBlitterChange() override;
void AcquireBlitterLock() override;
void ReleaseBlitterLock() override;
bool ClaimMousePointer() override;
void EditBoxGainedFocus() override;
void EditBoxLostFocus() override;
std::vector<int> GetListOfMonitorRefreshRates() 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.
@@ -94,10 +88,6 @@ private:
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 */

View File

@@ -23,12 +23,6 @@
#include "../window_func.h"
#include "sdl_v.h"
#include <SDL.h>
#include <mutex>
#include <condition_variable>
#if defined(__MINGW32__)
#include "../3rdparty/mingw-std-threads/mingw.mutex.h"
#include "../3rdparty/mingw-std-threads/mingw.condition_variable.h"
#endif
#include <algorithm>
#include "../safeguards.h"
@@ -39,14 +33,6 @@ 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
@@ -179,27 +165,6 @@ void VideoDriver_SDL::Paint()
}
}
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},
@@ -607,10 +572,10 @@ bool VideoDriver_SDL::PollEvent()
return true;
}
const char *VideoDriver_SDL::Start(const StringList &parm)
const char *VideoDriver_SDL::Start(const StringList &param)
{
char buf[30];
_use_hwpalette = GetDriverParamInt(parm, "hw_palette", 2);
_use_hwpalette = GetDriverParamInt(param, "hw_palette", 2);
/* Just on the offchance the audio subsystem started before the video system,
* check whether any part of SDL has been initialised before getting here.
@@ -636,7 +601,7 @@ const char *VideoDriver_SDL::Start(const StringList &parm)
MarkWholeScreenDirty();
SetupKeyboard();
_draw_threaded = !GetDriverParamBool(parm, "no_threads") && !GetDriverParamBool(parm, "no_thread");
this->is_game_threaded = !GetDriverParamBool(param, "no_threads") && !GetDriverParamBool(param, "no_thread");
return nullptr;
}
@@ -688,80 +653,25 @@ void VideoDriver_SDL::InputLoop()
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 ");
this->StartGameThread();
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;
}
this->StopGameThread();
}
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);
@@ -771,6 +681,7 @@ bool VideoDriver_SDL::ToggleFullscreen(bool fullscreen)
_fullscreen ^= true;
}
this->InvalidateGameOptionsWindow();
return ret;
}
@@ -779,25 +690,4 @@ 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 */

View File

@@ -29,30 +29,19 @@ public:
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. */

View File

@@ -8,50 +8,142 @@
/** @file video_driver.cpp Common code between video driver implementations. */
#include "../stdafx.h"
#include "../debug.h"
#include "../core/random_func.hpp"
#include "../network/network.h"
#include "../blitter/factory.hpp"
#include "../debug.h"
#include "../fontcache.h"
#include "../gfx_func.h"
#include "../gfxinit.h"
#include "../progress.h"
#include "../thread.h"
#include "../window_func.h"
#include "video_driver.hpp"
bool VideoDriver::Tick()
bool _video_hw_accel; ///< Whether to consider hardware accelerated video drivers.
void VideoDriver::GameLoop()
{
auto cur_ticks = std::chrono::steady_clock::now();
this->next_game_tick += this->GetGameInterval();
if (cur_ticks >= this->next_game_tick) {
this->next_game_tick += this->GetGameInterval();
/* Avoid next_game_tick getting behind more and more if it cannot keep up. */
if (this->next_game_tick < cur_ticks - ALLOWED_DRIFT * this->GetGameInterval()) this->next_game_tick = cur_ticks;
/* Avoid next_game_tick getting behind more and more if it cannot keep up. */
auto now = std::chrono::steady_clock::now();
if (this->next_game_tick < now - ALLOWED_DRIFT * this->GetGameInterval()) this->next_game_tick = now;
{
std::lock_guard<std::mutex> lock(this->game_state_mutex);
/* The game loop is the part that can run asynchronously.
* The rest except sleeping can't. */
this->UnlockVideoBuffer();
::GameLoop();
this->LockVideoBuffer();
// TODO: lock
::GameLoopPaletteAnimations();
// TODO: unlock
}
}
void VideoDriver::GameThread()
{
while (!_exit_game) {
this->GameLoop();
auto now = std::chrono::steady_clock::now();
if (this->next_game_tick > now) {
std::this_thread::sleep_for(this->next_game_tick - now);
} else {
/* Ensure we yield this thread if drawings wants to take a lock on
* the game state. This is mainly because most OSes have an
* optimization that if you unlock/lock a mutex in the same thread
* quickly, it will never context switch even if there is another
* thread waiting to take the lock on the same mutex. */
std::lock_guard<std::mutex> lock(this->game_thread_wait_mutex);
}
}
}
/**
* Pause the game-loop for a bit, releasing the game-state lock. This allows,
* if the draw-tick requested this, the drawing to happen.
*/
void VideoDriver::GameLoopPause()
{
/* If we are not called from the game-thread, ignore this request. */
if (std::this_thread::get_id() != this->game_thread.get_id()) return;
this->game_state_mutex.unlock();
{
/* See GameThread() for more details on this lock. */
std::lock_guard<std::mutex> lock(this->game_thread_wait_mutex);
}
this->game_state_mutex.lock();
}
/* static */ void VideoDriver::GameThreadThunk(VideoDriver *drv)
{
drv->GameThread();
}
void VideoDriver::StartGameThread()
{
if (this->is_game_threaded) {
this->is_game_threaded = StartNewThread(&this->game_thread, "ottd:game", &VideoDriver::GameThreadThunk, this);
}
DEBUG(driver, 1, "using %sthread for game-loop", this->is_game_threaded ? "" : "no ");
}
void VideoDriver::StopGameThread()
{
if (!this->is_game_threaded) return;
this->game_thread.join();
}
void VideoDriver::RealChangeBlitter(const char *repl_blitter)
{
const char *cur_blitter = BlitterFactory::GetCurrentBlitter()->GetName();
DEBUG(driver, 1, "Switching blitter from '%s' to '%s'... ", cur_blitter, repl_blitter);
Blitter *new_blitter = BlitterFactory::SelectBlitter(repl_blitter);
if (new_blitter == nullptr) NOT_REACHED();
DEBUG(driver, 1, "Successfully switched to %s.", repl_blitter);
if (!this->AfterBlitterChange()) {
/* Failed to switch blitter, let's hope we can return to the old one. */
if (BlitterFactory::SelectBlitter(cur_blitter) == nullptr || !this->AfterBlitterChange()) usererror("Failed to reinitialize video driver. Specify a fixed blitter in the config");
}
/* Clear caches that might have sprites for another blitter. */
this->ClearSystemSprites();
ClearFontCache();
GfxClearSpriteCache();
ReInitAllWindows();
}
void VideoDriver::Tick()
{
if (!this->is_game_threaded && std::chrono::steady_clock::now() >= this->next_game_tick) {
this->GameLoop();
/* For things like dedicated server, don't run a separate draw-tick. */
if (!this->HasGUI()) {
::InputLoop();
UpdateWindows();
::UpdateWindows();
this->next_draw_tick = this->next_game_tick;
}
}
/* Prevent drawing when switching mode, as windows can be removed when they should still appear. */
if (this->HasGUI() && cur_ticks >= this->next_draw_tick && (_switch_mode == SM_NONE || _game_mode == GM_BOOTSTRAP || HasModalProgress())) {
auto now = std::chrono::steady_clock::now();
if (this->HasGUI() && now >= this->next_draw_tick) {
this->next_draw_tick += this->GetDrawInterval();
/* Avoid next_draw_tick getting behind more and more if it cannot keep up. */
if (this->next_draw_tick < cur_ticks - ALLOWED_DRIFT * this->GetDrawInterval()) this->next_draw_tick = cur_ticks;
if (this->next_draw_tick < now - ALLOWED_DRIFT * this->GetDrawInterval()) this->next_draw_tick = now;
/* Keep the interactive randomizer a bit more random by requesting
* new values when-ever we can. */
InteractiveRandom();
while (this->PollEvent()) {}
this->InputLoop();
/* Check if the fast-forward button is still pressed. */
@@ -63,25 +155,51 @@ bool VideoDriver::Tick()
this->fast_forward_via_key = false;
}
::InputLoop();
UpdateWindows();
{
/* Tell the game-thread to stop so we can have a go. */
std::lock_guard<std::mutex> lock_wait(this->game_thread_wait_mutex);
std::lock_guard<std::mutex> lock_state(this->game_state_mutex);
this->LockVideoBuffer();
if (this->change_blitter != nullptr) {
this->RealChangeBlitter(this->change_blitter);
this->change_blitter = nullptr;
}
while (this->PollEvent()) {}
::InputLoop();
/* Prevent drawing when switching mode, as windows can be removed when they should still appear. */
if (_game_mode == GM_BOOTSTRAP || _switch_mode == SM_NONE || HasModalProgress()) {
::UpdateWindows();
}
this->PopulateSystemSprites();
}
this->CheckPaletteAnim();
this->Paint();
return true;
this->UnlockVideoBuffer();
}
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 next_tick = this->next_draw_tick;
auto now = std::chrono::steady_clock::now();
if (!this->is_game_threaded) {
next_tick = min(next_tick, this->next_game_tick);
}
if (next_tick > now) {
this->UnlockVideoBuffer();
std::this_thread::sleep_for(next_tick - now);
this->LockVideoBuffer();
}
}
void VideoDriver::InvalidateGameOptionsWindow()
{
InvalidateWindowClassesData(WC_GAME_OPTIONS, 3);
}

View File

@@ -16,13 +16,18 @@
#include "../gfx_func.h"
#include "../settings_type.h"
#include "../zoom_type.h"
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <mutex>
#include <thread>
#include <vector>
extern std::string _ini_videodriver;
extern std::vector<Dimension> _resolutions;
extern Dimension _cur_resolution;
extern bool _rightclick_emulate;
extern bool _video_hw_accel;
/** The base of all video drivers. */
class VideoDriver : public Driver {
@@ -30,6 +35,8 @@ class VideoDriver : public Driver {
const uint DEFAULT_WINDOW_HEIGHT = 480u; ///< Default window height.
public:
VideoDriver() : is_game_threaded(true), change_blitter(nullptr) {}
/**
* Mark a particular area dirty.
* @param left The left most line of the dirty area.
@@ -61,7 +68,6 @@ public:
/**
* Callback invoked after the blitter was changed.
* This may only be called between AcquireBlitterLock and ReleaseBlitterLock.
* @return True if no error.
*/
virtual bool AfterBlitterChange()
@@ -69,18 +75,6 @@ public:
return true;
}
/**
* Acquire any lock(s) required to be held when changing blitters.
* These lock(s) may not be acquired recursively.
*/
virtual void AcquireBlitterLock() { }
/**
* Release any lock(s) required to be held when changing blitters.
* These lock(s) may not be acquired recursively.
*/
virtual void ReleaseBlitterLock() { }
virtual bool ClaimMousePointer()
{
return true;
@@ -95,6 +89,11 @@ public:
return false;
}
/**
* Populate all sprites in cache.
*/
virtual void PopulateSystemSprites() {}
/**
* Clear all cached sprites.
*/
@@ -150,6 +149,15 @@ public:
*/
virtual void EditBoxGainedFocus() {}
/**
* Get a list of refresh rates of each available monitor.
* @return A vector of the refresh rates of all available monitors.
*/
virtual std::vector<int> GetListOfMonitorRefreshRates()
{
return {};
}
/**
* Get a suggested default GUI zoom taking screen DPI into account.
*/
@@ -162,6 +170,17 @@ public:
return ZOOM_LVL_OUT_4X;
}
/**
* Queue a request to change the blitter. This is not executed immediately,
* but instead on the next draw-tick.
*/
void ChangeBlitter(const char *new_blitter)
{
this->change_blitter = new_blitter;
}
void GameLoopPause();
/**
* Get the currently active instance of the video driver.
*/
@@ -240,11 +259,6 @@ protected:
*/
virtual void Paint() {}
/**
* Thread function for threaded drawing.
*/
virtual void PaintThread() {}
/**
* Process any pending palette animation.
*/
@@ -257,16 +271,29 @@ protected:
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.
* Start the loop for game-tick.
*/
bool Tick();
void StartGameThread();
/**
* Stop the loop for the game-tick. This can still tick at most one time before truly shutting down.
*/
void StopGameThread();
/**
* Give the video-driver a tick.
* It will process any potential game-tick and/or draw-tick, and/or any
* other video-driver related event.
*/
void Tick();
/**
* Sleep till the next tick is about to happen.
*/
void SleepTillNextTick();
void InvalidateGameOptionsWindow();
std::chrono::steady_clock::duration GetGameInterval()
{
/* If we are paused, run on normal speed. */
@@ -287,6 +314,20 @@ protected:
bool fast_forward_key_pressed; ///< The fast-forward key is being pressed.
bool fast_forward_via_key; ///< The fast-forward was enabled by key press.
bool is_game_threaded;
std::thread game_thread;
std::mutex game_state_mutex;
std::mutex game_thread_wait_mutex;
static void GameThreadThunk(VideoDriver *drv);
private:
void GameLoop();
void GameThread();
void RealChangeBlitter(const char *repl_blitter);
const char *change_blitter; ///< Request to change the blitter. nullptr if no pending request.
};
#endif /* VIDEO_VIDEO_DRIVER_HPP */

View File

@@ -129,9 +129,10 @@ uint8 VideoDriver_Win32Base::GetFullscreenBpp()
/**
* Instantiate a new window.
* @param full_screen Whether to make a full screen window or not.
* @param resize Whether to change window size.
* @return True if the window could be created.
*/
bool VideoDriver_Win32Base::MakeWindow(bool full_screen)
bool VideoDriver_Win32Base::MakeWindow(bool full_screen, bool resize)
{
/* full_screen is whether the new window should be fullscreen,
* _wnd.fullscreen is whether the current window is. */
@@ -173,7 +174,7 @@ bool VideoDriver_Win32Base::MakeWindow(bool full_screen)
}
if (ChangeDisplaySettings(&settings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) {
this->MakeWindow(false); // don't care about the result
this->MakeWindow(false, resize); // don't care about the result
return false; // the request failed
}
} else if (this->fullscreen) {
@@ -206,7 +207,7 @@ bool VideoDriver_Win32Base::MakeWindow(bool full_screen)
h = r.bottom - r.top;
if (this->main_wnd != nullptr) {
if (!_window_maximize) SetWindowPos(this->main_wnd, 0, 0, 0, w, h, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOMOVE);
if (!_window_maximize && resize) SetWindowPos(this->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;
@@ -226,11 +227,6 @@ bool VideoDriver_Win32Base::MakeWindow(bool full_screen)
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)
{
@@ -871,70 +867,16 @@ bool VideoDriver_Win32Base::PollEvent()
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);
}
}
}
this->StartGameThread();
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;
}
this->StopGameThread();
}
void VideoDriver_Win32Base::ClientSizeChanged(int w, int h, bool force)
@@ -954,9 +896,6 @@ void VideoDriver_Win32Base::ClientSizeChanged(int w, int h, bool force)
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;
@@ -967,32 +906,40 @@ bool VideoDriver_Win32Base::ChangeResolution(int w, int h)
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);
return this->MakeWindow(full_screen);
}
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();
this->InvalidateGameOptionsWindow();
return res;
}
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);
}
std::vector<int> VideoDriver_Win32Base::GetListOfMonitorRefreshRates()
{
std::vector<int> rates = {};
EnumDisplayMonitors(nullptr, nullptr, [](HMONITOR hMonitor, HDC hDC, LPRECT rc, LPARAM data) -> BOOL {
auto &list = *reinterpret_cast<std::vector<int>*>(data);
MONITORINFOEX monitorInfo = {};
monitorInfo.cbSize = sizeof(MONITORINFOEX);
GetMonitorInfo(hMonitor, &monitorInfo);
DEVMODE devMode = {};
devMode.dmSize = sizeof(DEVMODE);
devMode.dmDriverExtra = 0;
EnumDisplaySettings(monitorInfo.szDevice, ENUM_CURRENT_SETTINGS, &devMode);
if (devMode.dmDisplayFrequency != 0) list.push_back(devMode.dmDisplayFrequency);
return true;
}, reinterpret_cast<LPARAM>(&rates));
return rates;
}
Dimension VideoDriver_Win32Base::GetScreenSize() const
{
return { static_cast<uint>(GetSystemMetrics(SM_CXSCREEN)), static_cast<uint>(GetSystemMetrics(SM_CYSCREEN)) };
@@ -1043,8 +990,6 @@ 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);
@@ -1060,7 +1005,6 @@ void VideoDriver_Win32Base::UnlockVideoBuffer()
_screen.dst_ptr = nullptr;
}
if (this->draw_threaded) this->draw_lock.unlock();
this->buffer_locked = false;
}
@@ -1079,7 +1023,7 @@ const char *VideoDriver_Win32GDI::Start(const StringList &param)
MarkWholeScreenDirty();
this->draw_threaded = !GetDriverParam(param, "no_threads") && !GetDriverParam(param, "no_thread") && std::thread::hardware_concurrency() > 1;
this->is_game_threaded = !GetDriverParamBool(param, "no_threads") && !GetDriverParamBool(param, "no_thread");
return nullptr;
}
@@ -1130,7 +1074,7 @@ bool VideoDriver_Win32GDI::AllocateBackingStore(int w, int h, bool force)
bool VideoDriver_Win32GDI::AfterBlitterChange()
{
assert(BlitterFactory::GetCurrentBlitter()->GetScreenDepth() != 0);
return this->AllocateBackingStore(_screen.width, _screen.height, true) && this->MakeWindow(_fullscreen);
return this->AllocateBackingStore(_screen.width, _screen.height, true) && this->MakeWindow(_fullscreen, false);
}
void VideoDriver_Win32GDI::MakePalette()
@@ -1201,13 +1145,7 @@ void VideoDriver_Win32GDI::Paint()
break;
case Blitter::PALETTE_ANIMATION_BLITTER: {
bool need_buf = _screen.dst_ptr == nullptr;
if (need_buf) _screen.dst_ptr = this->GetVideoPointer();
blitter->PaletteAnimate(_local_palette);
if (need_buf) {
this->ReleaseVideoPointer();
_screen.dst_ptr = nullptr;
}
break;
}
@@ -1230,26 +1168,6 @@ void VideoDriver_Win32GDI::Paint()
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 */
@@ -1395,9 +1313,10 @@ const char *VideoDriver_Win32OpenGL::Start(const StringList &param)
this->ClientSizeChanged(this->width, this->height, true);
this->draw_threaded = false;
MarkWholeScreenDirty();
this->is_game_threaded = !GetDriverParamBool(param, "no_threads") && !GetDriverParamBool(param, "no_thread");
return nullptr;
}
@@ -1478,6 +1397,11 @@ bool VideoDriver_Win32OpenGL::AfterBlitterChange()
return true;
}
void VideoDriver_Win32OpenGL::PopulateSystemSprites()
{
OpenGLBackend::Get()->PopulateCursorCache();
}
void VideoDriver_Win32OpenGL::ClearSystemSprites()
{
OpenGLBackend::Get()->ClearCursorCache();

View File

@@ -21,7 +21,7 @@
/** 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;
@@ -33,14 +33,12 @@ public:
bool ToggleFullscreen(bool fullscreen) override;
void AcquireBlitterLock() override;
void ReleaseBlitterLock() override;
bool ClaimMousePointer() override;
void EditBoxLostFocus() override;
std::vector<int> GetListOfMonitorRefreshRates() override;
protected:
HWND main_wnd; ///< Handle to system window.
bool fullscreen; ///< Whether to use (true) fullscreen mode.
@@ -51,12 +49,7 @@ protected:
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;
@@ -67,7 +60,7 @@ protected:
bool PollEvent() override;
void Initialize();
bool MakeWindow(bool full_screen);
bool MakeWindow(bool full_screen, bool resize = true);
void ClientSizeChanged(int w, int h, bool force = false);
/** Get screen depth to use for fullscreen mode. */
@@ -82,10 +75,6 @@ protected:
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. */
@@ -108,7 +97,6 @@ protected:
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;
@@ -147,6 +135,8 @@ public:
bool UseSystemCursor() override { return true; }
void PopulateSystemSprites() override;
void ClearSystemSprites() override;
bool HasAnimBuffer() override { return true; }
@@ -163,7 +153,6 @@ protected:
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;
@@ -179,6 +168,9 @@ class FVideoDriver_Win32OpenGL : public DriverFactoryBase {
public:
FVideoDriver_Win32OpenGL() : DriverFactoryBase(Driver::DT_VIDEO, 10, "win32-opengl", "Win32 OpenGL Video Driver") {}
/* virtual */ Driver *CreateInstance() const override { return new VideoDriver_Win32OpenGL(); }
protected:
bool UsesHardwareAcceleration() const override { return true; }
};
#endif /* WITH_OPENGL */