From 0e56a73fb8002469b3d819d0ef75226fe312a7fe Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sun, 4 Jun 2023 16:40:17 +0200 Subject: [PATCH] Fix: disable hardware acceleration when GPU driver crashed the game last attempt (#10928) --- src/driver.cpp | 44 +++++++++++++++++++++++++++++++++++++- src/driver.h | 2 ++ src/lang/english.txt | 1 + src/video/video_driver.cpp | 8 +++++++ 4 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/driver.cpp b/src/driver.cpp index ea03604bbe..acb441309a 100644 --- a/src/driver.cpp +++ b/src/driver.cpp @@ -16,8 +16,15 @@ #include "video/video_driver.hpp" #include "string_func.h" #include "table/strings.h" +#include "fileio_func.h" #include +#ifdef _WIN32 +# include +#else +# include +#endif /* _WIN32 */ + #include "safeguards.h" std::string _ini_videodriver; ///< The video driver a stored in the configuration file. @@ -32,6 +39,8 @@ std::string _ini_musicdriver; ///< The music driver a stored in the confi std::string _ini_blitter; ///< The blitter as stored in the configuration file. bool _blitter_autodetected; ///< Was the blitter autodetected or specified by the user? +static const std::string HWACCELERATION_TEST_FILE = "hwaccel.dat"; ///< Filename to test if we crashed last time we tried to use hardware acceleration. + /** * Get a string parameter the list of parameters. * @param parm The parameters. @@ -114,6 +123,27 @@ bool DriverFactoryBase::SelectDriverImpl(const std::string &name, Driver::Type t if (type == Driver::DT_VIDEO && !_video_hw_accel && d->UsesHardwareAcceleration()) continue; + if (type == Driver::DT_VIDEO && _video_hw_accel && d->UsesHardwareAcceleration()) { + /* Check if we have already tried this driver in last run. + * If it is here, it most likely means we crashed. So skip + * hardware acceleration. */ + auto filename = FioFindFullPath(BASE_DIR, HWACCELERATION_TEST_FILE); + if (!filename.empty()) { + unlink(filename.c_str()); + + Debug(driver, 1, "Probing {} driver '{}' skipped due to earlier crash", GetDriverTypeName(type), d->name); + + _video_hw_accel = false; + ErrorMessageData msg(STR_VIDEO_DRIVER_ERROR, STR_VIDEO_DRIVER_ERROR_HARDWARE_ACCELERATION_CRASH, true); + ScheduleErrorMessage(msg); + continue; + } + + /* Write empty file to note we are attempting hardware acceleration. */ + auto f = FioFOpenFile(HWACCELERATION_TEST_FILE, "w", BASE_DIR); + FioFCloseFile(f); + } + Driver *oldd = *GetActiveDriver(type); Driver *newd = d->CreateInstance(); *GetActiveDriver(type) = newd; @@ -131,7 +161,7 @@ bool DriverFactoryBase::SelectDriverImpl(const std::string &name, Driver::Type t if (type == Driver::DT_VIDEO && _video_hw_accel && d->UsesHardwareAcceleration()) { _video_hw_accel = false; - ErrorMessageData msg(STR_VIDEO_DRIVER_ERROR, STR_VIDEO_DRIVER_ERROR_NO_HARDWARE_ACCELERATION); + ErrorMessageData msg(STR_VIDEO_DRIVER_ERROR, STR_VIDEO_DRIVER_ERROR_NO_HARDWARE_ACCELERATION, true); ScheduleErrorMessage(msg); } } @@ -177,6 +207,18 @@ bool DriverFactoryBase::SelectDriverImpl(const std::string &name, Driver::Type t } } +/** + * Mark the current video driver as operational. + */ +void DriverFactoryBase::MarkVideoDriverOperational() +{ + /* As part of the detection whether the GPU driver crashes the game, + * and as we are operational now, remove the hardware acceleration + * test-file. */ + auto filename = FioFindFullPath(BASE_DIR, HWACCELERATION_TEST_FILE); + if (!filename.empty()) unlink(filename.c_str()); +} + /** * Build a human readable list of available drivers, grouped by type. * @param output_iterator The iterator to write the string to. diff --git a/src/driver.h b/src/driver.h index 80b05b41bd..4683784266 100644 --- a/src/driver.h +++ b/src/driver.h @@ -100,6 +100,8 @@ private: static bool SelectDriverImpl(const std::string &name, Driver::Type type); + static void MarkVideoDriverOperational(); + protected: DriverFactoryBase(Driver::Type type, int priority, const char *name, const char *description); diff --git a/src/lang/english.txt b/src/lang/english.txt index 0c4e06aff6..36248f1edd 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2080,6 +2080,7 @@ STR_CONFIG_ERROR_SPRITECACHE_TOO_BIG :{WHITE}Allocati # Video initalization errors STR_VIDEO_DRIVER_ERROR :{WHITE}Error with video settings... STR_VIDEO_DRIVER_ERROR_NO_HARDWARE_ACCELERATION :{WHITE}... no compatible GPU found. Hardware acceleration disabled +STR_VIDEO_DRIVER_ERROR_HARDWARE_ACCELERATION_CRASH :{WHITE}... GPU driver crashed the game. Hardware acceleration disabled # Intro window STR_INTRO_CAPTION :{WHITE}OpenTTD {REV} diff --git a/src/video/video_driver.cpp b/src/video/video_driver.cpp index 8fe582760f..ad1c8fb808 100644 --- a/src/video/video_driver.cpp +++ b/src/video/video_driver.cpp @@ -12,6 +12,7 @@ #include "../network/network.h" #include "../blitter/factory.hpp" #include "../debug.h" +#include "../driver.h" #include "../fontcache.h" #include "../gfx_func.h" #include "../gfxinit.h" @@ -156,6 +157,13 @@ void VideoDriver::Tick() this->Paint(); this->UnlockVideoBuffer(); + + /* Wait till the first successful drawing tick before marking the driver as operational. */ + static bool first_draw_tick = true; + if (first_draw_tick) { + first_draw_tick = false; + DriverFactoryBase::MarkVideoDriverOperational(); + } } }