Implement a mechanism to survive from GPU driver crashes on Windows.

This commit is contained in:
Skyth 2025-03-27 15:38:11 +03:00
parent 4b4c439709
commit 7726afeb29
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>
@ -1651,7 +1652,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;
@ -1671,6 +1672,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
@ -1679,9 +1686,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)
{
@ -1718,6 +1733,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)
@ -1725,6 +1756,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

@ -200,6 +200,7 @@ int main(int argc, char *argv[])
bool forceInstaller = false;
bool forceDLCInstaller = false;
bool useDefaultWorkingDirectory = false;
bool graphicsApiRetry = false;
const char *sdlVideoDriver = nullptr;
for (uint32_t i = 1; i < argc; i++)
@ -207,6 +208,7 @@ int main(int argc, char *argv[])
forceInstaller = forceInstaller || (strcmp(argv[i], "--install") == 0);
forceDLCInstaller = forceDLCInstaller || (strcmp(argv[i], "--install-dlc") == 0);
useDefaultWorkingDirectory = useDefaultWorkingDirectory || (strcmp(argv[i], "--use-cwd") == 0);
graphicsApiRetry = graphicsApiRetry || (strcmp(argv[i], "--graphics-api-retry") == 0);
if (strcmp(argv[i], "--sdl-video-driver") == 0)
{
@ -261,7 +263,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);
@ -281,7 +283,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);