From 49d3b675edc579c2dcfb6a5254eb01dda12a8dd6 Mon Sep 17 00:00:00 2001 From: "Skyth (Asilkan)" <19259897+blueskythlikesclouds@users.noreply.github.com> Date: Sun, 30 Mar 2025 04:36:55 +0300 Subject: [PATCH] Implement a mechanism to survive from GPU driver crashes on Windows. (#1480) --- UnleashedRecomp/gpu/video.cpp | 45 ++++++++++++++++++++++++++++++++--- UnleashedRecomp/gpu/video.h | 2 +- UnleashedRecomp/main.cpp | 6 +++-- 3 files changed, 47 insertions(+), 6 deletions(-) diff --git a/UnleashedRecomp/gpu/video.cpp b/UnleashedRecomp/gpu/video.cpp index 6028de9..27b9609 100644 --- a/UnleashedRecomp/gpu/video.cpp +++ b/UnleashedRecomp/gpu/video.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #if defined(ASYNC_PSO_DEBUG) || defined(PSO_CACHING) #include @@ -1652,7 +1653,7 @@ static void ApplyLowEndDefaults() } } -bool Video::CreateHostDevice(const char *sdlVideoDriver) +bool Video::CreateHostDevice(const char *sdlVideoDriver, bool graphicsApiRetry) { for (uint32_t i = 0; i < 16; i++) g_inputSlots[i].index = i; @@ -1672,6 +1673,12 @@ bool Video::CreateHostDevice(const char *sdlVideoDriver) std::vector interfaceFunctions; #ifdef UNLEASHED_RECOMP_D3D12 + if (graphicsApiRetry) + { + // If we are attempting to create again after a reboot due to a crash, swap the order. + g_vulkan = !g_vulkan; + } + interfaceFunctions.push_back(g_vulkan ? CreateVulkanInterfaceWrapper : CreateD3D12Interface); interfaceFunctions.push_back(g_vulkan ? CreateD3D12Interface : CreateVulkanInterfaceWrapper); #else @@ -1680,9 +1687,17 @@ bool Video::CreateHostDevice(const char *sdlVideoDriver) for (RenderInterfaceFunction *interfaceFunction : interfaceFunctions) { - g_interface = interfaceFunction(); - if (g_interface != nullptr) +#ifdef UNLEASHED_RECOMP_D3D12 + // Wrap the device creation in __try/__except to survive from driver crashes. + __try +#endif { + g_interface = interfaceFunction(); + if (g_interface == nullptr) + { + continue; + } + g_device = g_interface->createDevice(Config::GraphicsDevice); if (g_device != nullptr) { @@ -1719,6 +1734,22 @@ bool Video::CreateHostDevice(const char *sdlVideoDriver) break; } } +#ifdef UNLEASHED_RECOMP_D3D12 + __except (EXCEPTION_EXECUTE_HANDLER) + { + if (graphicsApiRetry) + { + // If we were retrying, and this also failed, then we'll show the user neither of the graphics APIs succeeded. + return false; + } + else + { + // If this is the first crash we ran into, reboot and try the other graphics API. + os::process::StartProcess(os::process::GetExecutablePath(), { "--graphics-api-retry" }); + std::_Exit(0); + } + } +#endif } if (g_device == nullptr) @@ -1726,6 +1757,14 @@ bool Video::CreateHostDevice(const char *sdlVideoDriver) return false; } +#ifdef UNLEASHED_RECOMP_D3D12 + if (graphicsApiRetry) + { + // If we managed to create a device after retrying it in a reboot, remember the one we picked. + Config::GraphicsAPI = g_vulkan ? EGraphicsAPI::Vulkan : EGraphicsAPI::D3D12; + } +#endif + g_capabilities = g_device->getCapabilities(); LoadEmbeddedResources(); diff --git a/UnleashedRecomp/gpu/video.h b/UnleashedRecomp/gpu/video.h index 3a394be..b07169b 100644 --- a/UnleashedRecomp/gpu/video.h +++ b/UnleashedRecomp/gpu/video.h @@ -18,7 +18,7 @@ struct Video static inline uint32_t s_viewportWidth; static inline uint32_t s_viewportHeight; - static bool CreateHostDevice(const char *sdlVideoDriver); + static bool CreateHostDevice(const char *sdlVideoDriver, bool graphicsApiRetry); static void WaitOnSwapChain(); static void Present(); static void StartPipelinePrecompilation(); diff --git a/UnleashedRecomp/main.cpp b/UnleashedRecomp/main.cpp index 7cf0747..04e3fb1 100644 --- a/UnleashedRecomp/main.cpp +++ b/UnleashedRecomp/main.cpp @@ -208,6 +208,7 @@ int main(int argc, char *argv[]) bool forceDLCInstaller = false; bool useDefaultWorkingDirectory = false; bool forceInstallationCheck = false; + bool graphicsApiRetry = false; const char *sdlVideoDriver = nullptr; for (uint32_t i = 1; i < argc; i++) @@ -216,6 +217,7 @@ int main(int argc, char *argv[]) forceDLCInstaller = forceDLCInstaller || (strcmp(argv[i], "--install-dlc") == 0); useDefaultWorkingDirectory = useDefaultWorkingDirectory || (strcmp(argv[i], "--use-cwd") == 0); forceInstallationCheck = forceInstallationCheck || (strcmp(argv[i], "--install-check") == 0); + graphicsApiRetry = graphicsApiRetry || (strcmp(argv[i], "--graphics-api-retry") == 0); if (strcmp(argv[i], "--sdl-video-driver") == 0) { @@ -326,7 +328,7 @@ int main(int argc, char *argv[]) bool runInstallerWizard = forceInstaller || forceDLCInstaller || !isGameInstalled; if (runInstallerWizard) { - if (!Video::CreateHostDevice(sdlVideoDriver)) + if (!Video::CreateHostDevice(sdlVideoDriver, graphicsApiRetry)) { SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, GameWindow::GetTitle(), Localise("Video_BackendError").c_str(), GameWindow::s_pWindow); std::_Exit(1); @@ -346,7 +348,7 @@ int main(int argc, char *argv[]) if (!runInstallerWizard) { - if (!Video::CreateHostDevice(sdlVideoDriver)) + if (!Video::CreateHostDevice(sdlVideoDriver, graphicsApiRetry)) { SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, GameWindow::GetTitle(), Localise("Video_BackendError").c_str(), GameWindow::s_pWindow); std::_Exit(1);