Implement a mechanism to survive from GPU driver crashes on Windows. (#1480)
Some checks are pending
validate-internal / build (push) Waiting to run

This commit is contained in:
Skyth (Asilkan) 2025-03-30 04:36:55 +03:00 committed by GitHub
parent b282cbc4a2
commit 49d3b675ed
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 47 additions and 6 deletions

View file

@ -31,6 +31,7 @@
#include <user/config.h>
#include <sdl_listener.h>
#include <xxHashMap.h>
#include <os/process.h>
#if defined(ASYNC_PSO_DEBUG) || defined(PSO_CACHING)
#include <magic_enum/magic_enum.hpp>
@ -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<RenderInterfaceFunction *> 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();

View file

@ -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();

View file

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