From 568a0ac3974d837d7114e4480691b1d717451be2 Mon Sep 17 00:00:00 2001
From: lat9nq <lat9nq@gmail.com>
Date: Sun, 10 Jul 2022 11:46:53 -0400
Subject: [PATCH 1/7] yuzu: Rename check_vulkan to startup_checks

---
 src/yuzu/CMakeLists.txt                           | 4 ++--
 src/yuzu/main.cpp                                 | 2 +-
 src/yuzu/{check_vulkan.cpp => startup_checks.cpp} | 0
 src/yuzu/{check_vulkan.h => startup_checks.h}     | 0
 4 files changed, 3 insertions(+), 3 deletions(-)
 rename src/yuzu/{check_vulkan.cpp => startup_checks.cpp} (100%)
 rename src/yuzu/{check_vulkan.h => startup_checks.h} (100%)

diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 242867a4f0..534e553550 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -30,8 +30,6 @@ add_executable(yuzu
     applets/qt_web_browser_scripts.h
     bootmanager.cpp
     bootmanager.h
-    check_vulkan.cpp
-    check_vulkan.h
     compatdb.ui
     compatibility_list.cpp
     compatibility_list.h
@@ -155,6 +153,8 @@ add_executable(yuzu
     main.cpp
     main.h
     main.ui
+    startup_checks.cpp
+    startup_checks.h
     uisettings.cpp
     uisettings.h
     util/controller_navigation.cpp
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index b460020b16..64be8bf614 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -115,7 +115,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
 #include "video_core/shader_notify.h"
 #include "yuzu/about_dialog.h"
 #include "yuzu/bootmanager.h"
-#include "yuzu/check_vulkan.h"
 #include "yuzu/compatdb.h"
 #include "yuzu/compatibility_list.h"
 #include "yuzu/configuration/config.h"
@@ -131,6 +130,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
 #include "yuzu/install_dialog.h"
 #include "yuzu/loading_screen.h"
 #include "yuzu/main.h"
+#include "yuzu/startup_checks.h"
 #include "yuzu/uisettings.h"
 
 using namespace Common::Literals;
diff --git a/src/yuzu/check_vulkan.cpp b/src/yuzu/startup_checks.cpp
similarity index 100%
rename from src/yuzu/check_vulkan.cpp
rename to src/yuzu/startup_checks.cpp
diff --git a/src/yuzu/check_vulkan.h b/src/yuzu/startup_checks.h
similarity index 100%
rename from src/yuzu/check_vulkan.h
rename to src/yuzu/startup_checks.h

From 4f15d9ed6fba4fa1804c5be3f9378e3ad3d32688 Mon Sep 17 00:00:00 2001
From: lat9nq <lat9nq@gmail.com>
Date: Sun, 10 Jul 2022 14:08:20 -0400
Subject: [PATCH 2/7] yuzu: Check Vulkan on startup with a child

---
 src/yuzu/main.cpp           | 15 +++++++++
 src/yuzu/startup_checks.cpp | 61 ++++++++++++++++++++++++++++++++++++-
 src/yuzu/startup_checks.h   |  3 ++
 3 files changed, 78 insertions(+), 1 deletion(-)

diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 64be8bf614..f2e449560f 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -3853,6 +3853,21 @@ void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) {
 #endif
 
 int main(int argc, char* argv[]) {
+#ifdef _WIN32
+    char variable_contents[32];
+    const DWORD startup_check_var =
+        GetEnvironmentVariable(STARTUP_CHECK_ENV_VAR, variable_contents, 32);
+    if (startup_check_var != 0) {
+        std::fprintf(stderr, "perform statup checks\n");
+        CheckVulkan();
+        return 0;
+    } else {
+        std::fprintf(stderr, "%d\n", StartupChecks());
+    }
+#elif YUZU_UNIX
+#error "Unimplemented"
+#endif
+
     Common::DetachedTasks detached_tasks;
     MicroProfileOnThreadCreate("Frontend");
     SCOPE_EXIT({ MicroProfileShutdown(); });
diff --git a/src/yuzu/startup_checks.cpp b/src/yuzu/startup_checks.cpp
index e6d66ab349..cfd14a6d5f 100644
--- a/src/yuzu/startup_checks.cpp
+++ b/src/yuzu/startup_checks.cpp
@@ -3,6 +3,15 @@
 
 #include "video_core/vulkan_common/vulkan_wrapper.h"
 
+#ifdef _WIN32
+#include <cstring> // for memset, strncpy
+#include <processthreadsapi.h>
+#include <windows.h>
+#elif defined(YUZU_UNIX)
+#include <unistd.h>
+#endif
+
+#include <cstdio>
 #include <filesystem>
 #include <fstream>
 #include "common/fs/fs.h"
@@ -10,7 +19,7 @@
 #include "common/logging/log.h"
 #include "video_core/vulkan_common/vulkan_instance.h"
 #include "video_core/vulkan_common/vulkan_library.h"
-#include "yuzu/check_vulkan.h"
+#include "yuzu/startup_checks.h"
 #include "yuzu/uisettings.h"
 
 constexpr char TEMP_FILE_NAME[] = "vulkan_check";
@@ -51,3 +60,53 @@ bool CheckVulkan() {
     std::filesystem::remove(temp_file_loc);
     return true;
 }
+
+bool StartupChecks() {
+#ifdef _WIN32
+    const bool env_var_set = SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, "ON");
+    if (!env_var_set) {
+        LOG_ERROR(Frontend, "SetEnvironmentVariableA failed to set {}, {}", STARTUP_CHECK_ENV_VAR,
+                  GetLastError());
+        return false;
+    }
+
+    STARTUPINFOA startup_info;
+    PROCESS_INFORMATION process_info;
+
+    std::memset(&startup_info, '\0', sizeof(startup_info));
+    std::memset(&process_info, '\0', sizeof(process_info));
+    startup_info.cb = sizeof(startup_info);
+
+    char p_name[255];
+    std::strncpy(p_name, "yuzu.exe", 255);
+
+    // TODO: use argv[0] instead of yuzu.exe
+    const bool process_created = CreateProcessA(nullptr,       // lpApplicationName
+                                                p_name,        // lpCommandLine
+                                                nullptr,       // lpProcessAttributes
+                                                nullptr,       // lpThreadAttributes
+                                                false,         // bInheritHandles
+                                                0,             // dwCreationFlags
+                                                nullptr,       // lpEnvironment
+                                                nullptr,       // lpCurrentDirectory
+                                                &startup_info, // lpStartupInfo
+                                                &process_info  // lpProcessInformation
+    );
+    if (!process_created) {
+        LOG_ERROR(Frontend, "CreateProcessA failed, {}", GetLastError());
+        return false;
+    }
+
+    // wait until the processs exits
+    DWORD exit_code = STILL_ACTIVE;
+    while (exit_code == STILL_ACTIVE) {
+        GetExitCodeProcess(process_info.hProcess, &exit_code);
+    }
+
+    std::fprintf(stderr, "exit code: %d\n", exit_code);
+
+    CloseHandle(process_info.hProcess);
+    CloseHandle(process_info.hThread);
+#endif
+    return true;
+}
diff --git a/src/yuzu/startup_checks.h b/src/yuzu/startup_checks.h
index e4ea935822..98bd5f4bff 100644
--- a/src/yuzu/startup_checks.h
+++ b/src/yuzu/startup_checks.h
@@ -3,4 +3,7 @@
 
 #pragma once
 
+constexpr char STARTUP_CHECK_ENV_VAR[] = "YUZU_DO_STARTUP_CHECKS";
+
 bool CheckVulkan();
+bool StartupChecks();

From 33abdfff9bf0a549b0d1d327e914e9a1ab4b799b Mon Sep 17 00:00:00 2001
From: lat9nq <lat9nq@gmail.com>
Date: Sun, 10 Jul 2022 16:10:35 -0400
Subject: [PATCH 3/7] yuzu: Simplify broken Vulkan handling

---
 src/yuzu/configuration/config.cpp             |  8 --
 src/yuzu/configuration/configure_graphics.cpp | 37 ++-------
 src/yuzu/configuration/configure_graphics.h   |  2 +-
 src/yuzu/configuration/configure_graphics.ui  |  9 +--
 src/yuzu/main.cpp                             | 30 ++++---
 src/yuzu/main.h                               |  2 +-
 src/yuzu/startup_checks.cpp                   | 78 ++++++++-----------
 src/yuzu/startup_checks.h                     | 12 ++-
 src/yuzu/uisettings.h                         |  2 +-
 9 files changed, 65 insertions(+), 115 deletions(-)

diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 9686412d07..ca10e9f236 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -682,12 +682,6 @@ void Config::ReadRendererValues() {
     ReadGlobalSetting(Settings::values.bg_green);
     ReadGlobalSetting(Settings::values.bg_blue);
 
-    if (!global && UISettings::values.has_broken_vulkan &&
-        Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::Vulkan &&
-        !Settings::values.renderer_backend.UsingGlobal()) {
-        Settings::values.renderer_backend.SetGlobal(true);
-    }
-
     if (global) {
         ReadBasicSetting(Settings::values.renderer_debug);
         ReadBasicSetting(Settings::values.renderer_shader_feedback);
@@ -807,7 +801,6 @@ void Config::ReadUIValues() {
     ReadBasicSetting(UISettings::values.pause_when_in_background);
     ReadBasicSetting(UISettings::values.mute_when_in_background);
     ReadBasicSetting(UISettings::values.hide_mouse);
-    ReadBasicSetting(UISettings::values.has_broken_vulkan);
     ReadBasicSetting(UISettings::values.disable_web_applet);
 
     qt_config->endGroup();
@@ -1355,7 +1348,6 @@ void Config::SaveUIValues() {
     WriteBasicSetting(UISettings::values.pause_when_in_background);
     WriteBasicSetting(UISettings::values.mute_when_in_background);
     WriteBasicSetting(UISettings::values.hide_mouse);
-    WriteBasicSetting(UISettings::values.has_broken_vulkan);
     WriteBasicSetting(UISettings::values.disable_web_applet);
 
     qt_config->endGroup();
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index 85f34dc35f..6b33c45351 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -58,24 +58,9 @@ ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* paren
         UpdateBackgroundColorButton(new_bg_color);
     });
 
-    connect(ui->button_check_vulkan, &QAbstractButton::clicked, this, [this] {
-        UISettings::values.has_broken_vulkan = false;
-
-        if (RetrieveVulkanDevices()) {
-            ui->api->setEnabled(true);
-            ui->button_check_vulkan->hide();
-
-            for (const auto& device : vulkan_devices) {
-                ui->device->addItem(device);
-            }
-        } else {
-            UISettings::values.has_broken_vulkan = true;
-        }
-    });
-
-    ui->api->setEnabled(!UISettings::values.has_broken_vulkan.GetValue());
-    ui->button_check_vulkan->setVisible(UISettings::values.has_broken_vulkan.GetValue());
-
+    ui->api->setEnabled(!UISettings::values.has_broken_vulkan);
+    ui->api_widget->setEnabled(!UISettings::values.has_broken_vulkan ||
+                               Settings::IsConfiguringGlobal());
     ui->bg_label->setVisible(Settings::IsConfiguringGlobal());
     ui->bg_combobox->setVisible(!Settings::IsConfiguringGlobal());
 }
@@ -315,7 +300,7 @@ void ConfigureGraphics::UpdateAPILayout() {
         vulkan_device = Settings::values.vulkan_device.GetValue(true);
         shader_backend = Settings::values.shader_backend.GetValue(true);
         ui->device_widget->setEnabled(false);
-        ui->backend_widget->setEnabled(UISettings::values.has_broken_vulkan.GetValue());
+        ui->backend_widget->setEnabled(false);
     } else {
         vulkan_device = Settings::values.vulkan_device.GetValue();
         shader_backend = Settings::values.shader_backend.GetValue();
@@ -337,9 +322,9 @@ void ConfigureGraphics::UpdateAPILayout() {
     }
 }
 
-bool ConfigureGraphics::RetrieveVulkanDevices() try {
+void ConfigureGraphics::RetrieveVulkanDevices() try {
     if (UISettings::values.has_broken_vulkan) {
-        return false;
+        return;
     }
 
     using namespace Vulkan;
@@ -355,11 +340,8 @@ bool ConfigureGraphics::RetrieveVulkanDevices() try {
         const std::string name = vk::PhysicalDevice(device, dld).GetProperties().deviceName;
         vulkan_devices.push_back(QString::fromStdString(name));
     }
-
-    return true;
 } catch (const Vulkan::vk::Exception& exception) {
     LOG_ERROR(Frontend, "Failed to enumerate devices with error: {}", exception.what());
-    return false;
 }
 
 Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const {
@@ -440,11 +422,4 @@ void ConfigureGraphics::SetupPerGameUI() {
         ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true)));
     ConfigurationShared::InsertGlobalItem(
         ui->nvdec_emulation, static_cast<int>(Settings::values.nvdec_emulation.GetValue(true)));
-
-    if (UISettings::values.has_broken_vulkan) {
-        ui->backend_widget->setEnabled(true);
-        ConfigurationShared::SetColoredComboBox(
-            ui->backend, ui->backend_widget,
-            static_cast<int>(Settings::values.shader_backend.GetValue(true)));
-    }
 }
diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h
index 8438f01876..1b101c9405 100644
--- a/src/yuzu/configuration/configure_graphics.h
+++ b/src/yuzu/configuration/configure_graphics.h
@@ -41,7 +41,7 @@ private:
     void UpdateDeviceSelection(int device);
     void UpdateShaderBackendSelection(int backend);
 
-    bool RetrieveVulkanDevices();
+    void RetrieveVulkanDevices();
 
     void SetupPerGameUI();
 
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index 2f94c94bca..1e4f74704b 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -6,7 +6,7 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>471</width>
+    <width>541</width>
     <height>759</height>
    </rect>
   </property>
@@ -574,13 +574,6 @@
      </property>
     </spacer>
    </item>
-   <item>
-    <widget class="QPushButton" name="button_check_vulkan">
-     <property name="text">
-      <string>Check for Working Vulkan</string>
-     </property>
-    </widget>
-   </item>
   </layout>
  </widget>
  <resources/>
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index f2e449560f..a2b11fdbf4 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -252,7 +252,7 @@ static QString PrettyProductName() {
     return QSysInfo::prettyProductName();
 }
 
-GMainWindow::GMainWindow()
+GMainWindow::GMainWindow(bool has_broken_vulkan)
     : ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()},
       input_subsystem{std::make_shared<InputCommon::InputSubsystem>()},
       config{std::make_unique<Config>(*system)},
@@ -352,17 +352,15 @@ GMainWindow::GMainWindow()
 
     MigrateConfigFiles();
 
-    if (!CheckVulkan()) {
-        config->Save();
+    if (has_broken_vulkan) {
+        UISettings::values.has_broken_vulkan = true;
+
+        QMessageBox::warning(this, tr("Broken Vulkan Installation Detected"),
+                             tr("Vulkan initialization failed during boot.<br><br>Click <a "
+                                "href='https://yuzu-emu.org/wiki/faq/"
+                                "#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>"
+                                "here for instructions to fix the issue</a>."));
 
-        QMessageBox::warning(
-            this, tr("Broken Vulkan Installation Detected"),
-            tr("Vulkan initialization failed on the previous boot.<br><br>Click <a "
-               "href='https://yuzu-emu.org/wiki/faq/"
-               "#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for "
-               "instructions to fix the issue</a>."));
-    }
-    if (UISettings::values.has_broken_vulkan) {
         Settings::values.renderer_backend = Settings::RendererBackend::OpenGL;
 
         renderer_status_button->setDisabled(true);
@@ -3853,17 +3851,17 @@ void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) {
 #endif
 
 int main(int argc, char* argv[]) {
+    bool has_broken_vulkan = false;
 #ifdef _WIN32
     char variable_contents[32];
     const DWORD startup_check_var =
         GetEnvironmentVariable(STARTUP_CHECK_ENV_VAR, variable_contents, 32);
-    if (startup_check_var != 0) {
-        std::fprintf(stderr, "perform statup checks\n");
+    const std::string variable_contents_s{variable_contents};
+    if (startup_check_var > 0 && variable_contents_s == "ON") {
         CheckVulkan();
         return 0;
-    } else {
-        std::fprintf(stderr, "%d\n", StartupChecks());
     }
+    StartupChecks(argv[0], &has_broken_vulkan);
 #elif YUZU_UNIX
 #error "Unimplemented"
 #endif
@@ -3907,7 +3905,7 @@ int main(int argc, char* argv[]) {
     // generating shaders
     setlocale(LC_ALL, "C");
 
-    GMainWindow main_window{};
+    GMainWindow main_window{has_broken_vulkan};
     // After settings have been loaded by GMainWindow, apply the filter
     main_window.show();
 
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 8cf224c9c2..7df6560025 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -118,7 +118,7 @@ class GMainWindow : public QMainWindow {
 public:
     void filterBarSetChecked(bool state);
     void UpdateUITheme();
-    explicit GMainWindow();
+    explicit GMainWindow(bool has_broken_vulkan);
     ~GMainWindow() override;
 
     bool DropAction(QDropEvent* event);
diff --git a/src/yuzu/startup_checks.cpp b/src/yuzu/startup_checks.cpp
index cfd14a6d5f..c860f0aa03 100644
--- a/src/yuzu/startup_checks.cpp
+++ b/src/yuzu/startup_checks.cpp
@@ -22,29 +22,7 @@
 #include "yuzu/startup_checks.h"
 #include "yuzu/uisettings.h"
 
-constexpr char TEMP_FILE_NAME[] = "vulkan_check";
-
-bool CheckVulkan() {
-    if (UISettings::values.has_broken_vulkan) {
-        return true;
-    }
-
-    LOG_DEBUG(Frontend, "Checking presence of Vulkan");
-
-    const auto fs_config_loc = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir);
-    const auto temp_file_loc = fs_config_loc / TEMP_FILE_NAME;
-
-    if (std::filesystem::exists(temp_file_loc)) {
-        LOG_WARNING(Frontend, "Detected recovery from previous failed Vulkan initialization");
-
-        UISettings::values.has_broken_vulkan = true;
-        std::filesystem::remove(temp_file_loc);
-        return false;
-    }
-
-    std::ofstream temp_file_handle(temp_file_loc);
-    temp_file_handle.close();
-
+void CheckVulkan() {
     try {
         Vulkan::vk::InstanceDispatch dld;
         const Common::DynamicLibrary library = Vulkan::OpenLibrary();
@@ -53,32 +31,48 @@ bool CheckVulkan() {
 
     } catch (const Vulkan::vk::Exception& exception) {
         LOG_ERROR(Frontend, "Failed to initialize Vulkan: {}", exception.what());
-        // Don't set has_broken_vulkan to true here: we care when loading Vulkan crashes the
-        // application, not when we can handle it.
     }
-
-    std::filesystem::remove(temp_file_loc);
-    return true;
 }
 
-bool StartupChecks() {
+bool StartupChecks(const char* arg0, bool* has_broken_vulkan) {
 #ifdef _WIN32
     const bool env_var_set = SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, "ON");
     if (!env_var_set) {
-        LOG_ERROR(Frontend, "SetEnvironmentVariableA failed to set {}, {}", STARTUP_CHECK_ENV_VAR,
-                  GetLastError());
+        std::fprintf(stderr, "SetEnvironmentVariableA failed to set %s, %d\n",
+                     STARTUP_CHECK_ENV_VAR, GetLastError());
         return false;
     }
 
-    STARTUPINFOA startup_info;
     PROCESS_INFORMATION process_info;
+    std::memset(&process_info, '\0', sizeof(process_info));
+
+    if (!SpawnChild(arg0, &process_info)) {
+        return false;
+    }
+
+    // wait until the processs exits
+    DWORD exit_code = STILL_ACTIVE;
+    while (exit_code == STILL_ACTIVE) {
+        GetExitCodeProcess(process_info.hProcess, &exit_code);
+    }
+
+    *has_broken_vulkan = (exit_code != 0);
+
+    CloseHandle(process_info.hProcess);
+    CloseHandle(process_info.hThread);
+#endif
+    return true;
+}
+
+#ifdef _WIN32
+bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi) {
+    STARTUPINFOA startup_info;
 
     std::memset(&startup_info, '\0', sizeof(startup_info));
-    std::memset(&process_info, '\0', sizeof(process_info));
     startup_info.cb = sizeof(startup_info);
 
     char p_name[255];
-    std::strncpy(p_name, "yuzu.exe", 255);
+    std::strncpy(p_name, arg0, 255);
 
     // TODO: use argv[0] instead of yuzu.exe
     const bool process_created = CreateProcessA(nullptr,       // lpApplicationName
@@ -90,23 +84,13 @@ bool StartupChecks() {
                                                 nullptr,       // lpEnvironment
                                                 nullptr,       // lpCurrentDirectory
                                                 &startup_info, // lpStartupInfo
-                                                &process_info  // lpProcessInformation
+                                                pi             // lpProcessInformation
     );
     if (!process_created) {
-        LOG_ERROR(Frontend, "CreateProcessA failed, {}", GetLastError());
+        std::fprintf(stderr, "CreateProcessA failed, %d\n", GetLastError());
         return false;
     }
 
-    // wait until the processs exits
-    DWORD exit_code = STILL_ACTIVE;
-    while (exit_code == STILL_ACTIVE) {
-        GetExitCodeProcess(process_info.hProcess, &exit_code);
-    }
-
-    std::fprintf(stderr, "exit code: %d\n", exit_code);
-
-    CloseHandle(process_info.hProcess);
-    CloseHandle(process_info.hThread);
-#endif
     return true;
 }
+#endif
diff --git a/src/yuzu/startup_checks.h b/src/yuzu/startup_checks.h
index 98bd5f4bff..096dd54a89 100644
--- a/src/yuzu/startup_checks.h
+++ b/src/yuzu/startup_checks.h
@@ -3,7 +3,15 @@
 
 #pragma once
 
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
 constexpr char STARTUP_CHECK_ENV_VAR[] = "YUZU_DO_STARTUP_CHECKS";
 
-bool CheckVulkan();
-bool StartupChecks();
+void CheckVulkan();
+bool StartupChecks(const char* arg0, bool* has_broken_vulkan);
+
+#ifdef _WIN32
+bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi);
+#endif
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index 044d88ca6a..2f6948243d 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -78,7 +78,7 @@ struct Values {
     Settings::Setting<bool> mute_when_in_background{false, "muteWhenInBackground"};
     Settings::Setting<bool> hide_mouse{true, "hideInactiveMouse"};
     // Set when Vulkan is known to crash the application
-    Settings::Setting<bool> has_broken_vulkan{false, "has_broken_vulkan"};
+    bool has_broken_vulkan = false;
 
     Settings::Setting<bool> select_user_on_boot{false, "select_user_on_boot"};
 

From fd4e48f96e8328bbace2d4df4f2a6f7ab9a699f1 Mon Sep 17 00:00:00 2001
From: lat9nq <lat9nq@gmail.com>
Date: Sun, 10 Jul 2022 17:01:37 -0400
Subject: [PATCH 4/7] startup_checks: Implement unix side code

Wow fork() is nice, isn't it?
---
 src/yuzu/main.cpp           | 12 +--------
 src/yuzu/startup_checks.cpp | 53 ++++++++++++++++++++++++++++++++-----
 2 files changed, 48 insertions(+), 17 deletions(-)

diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index a2b11fdbf4..69f9cdd9ca 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -3852,19 +3852,9 @@ void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) {
 
 int main(int argc, char* argv[]) {
     bool has_broken_vulkan = false;
-#ifdef _WIN32
-    char variable_contents[32];
-    const DWORD startup_check_var =
-        GetEnvironmentVariable(STARTUP_CHECK_ENV_VAR, variable_contents, 32);
-    const std::string variable_contents_s{variable_contents};
-    if (startup_check_var > 0 && variable_contents_s == "ON") {
-        CheckVulkan();
+    if (StartupChecks(argv[0], &has_broken_vulkan)) {
         return 0;
     }
-    StartupChecks(argv[0], &has_broken_vulkan);
-#elif YUZU_UNIX
-#error "Unimplemented"
-#endif
 
     Common::DetachedTasks detached_tasks;
     MicroProfileOnThreadCreate("Frontend");
diff --git a/src/yuzu/startup_checks.cpp b/src/yuzu/startup_checks.cpp
index c860f0aa03..6bd895400a 100644
--- a/src/yuzu/startup_checks.cpp
+++ b/src/yuzu/startup_checks.cpp
@@ -8,6 +8,8 @@
 #include <processthreadsapi.h>
 #include <windows.h>
 #elif defined(YUZU_UNIX)
+#include <errno.h>
+#include <sys/wait.h>
 #include <unistd.h>
 #endif
 
@@ -36,9 +38,20 @@ void CheckVulkan() {
 
 bool StartupChecks(const char* arg0, bool* has_broken_vulkan) {
 #ifdef _WIN32
+    // Check environment variable to see if we are the child
+    char variable_contents[32];
+    const DWORD startup_check_var =
+        GetEnvironmentVariable(STARTUP_CHECK_ENV_VAR, variable_contents, 32);
+    const std::string variable_contents_s{variable_contents};
+    if (startup_check_var > 0 && variable_contents_s == "ON") {
+        CheckVulkan();
+        return true;
+    }
+
+    // Set the startup variable for child processes
     const bool env_var_set = SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, "ON");
     if (!env_var_set) {
-        std::fprintf(stderr, "SetEnvironmentVariableA failed to set %s, %d\n",
+        std::fprintf(stderr, "SetEnvironmentVariableA failed to set %s with error %d\n",
                      STARTUP_CHECK_ENV_VAR, GetLastError());
         return false;
     }
@@ -53,15 +66,43 @@ bool StartupChecks(const char* arg0, bool* has_broken_vulkan) {
     // wait until the processs exits
     DWORD exit_code = STILL_ACTIVE;
     while (exit_code == STILL_ACTIVE) {
-        GetExitCodeProcess(process_info.hProcess, &exit_code);
+        const int err = GetExitCodeProcess(process_info.hProcess, &exit_code);
+        if (err == 0) {
+            std::fprintf(stderr, "GetExitCodeProcess failed with error %d\n", GetLastError());
+            break;
+        }
     }
 
     *has_broken_vulkan = (exit_code != 0);
 
-    CloseHandle(process_info.hProcess);
-    CloseHandle(process_info.hThread);
+    if (CloseHandle(process_info.hProcess) == 0) {
+        std::fprintf(stderr, "CloseHandle failed with error %d\n", GetLastError());
+    }
+    if (CloseHandle(process_info.hThread) == 0) {
+        std::fprintf(stderr, "CloseHandle failed with error %d\n", GetLastError());
+    }
+
+#elif defined(YUZU_UNIX)
+    const pid_t pid = fork();
+    if (pid == 0) {
+        CheckVulkan();
+        return true;
+    } else if (pid == -1) {
+        const int err = errno;
+        std::fprintf(stderr, "fork failed with error %d\n", err);
+        return false;
+    }
+
+    int status;
+    const int r_val = wait(&status);
+    if (r_val == -1) {
+        const int err = errno;
+        std::fprintf(stderr, "wait failed with error %d\n", err);
+        return false;
+    }
+    *has_broken_vulkan = (status != 0);
 #endif
-    return true;
+    return false;
 }
 
 #ifdef _WIN32
@@ -87,7 +128,7 @@ bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi) {
                                                 pi             // lpProcessInformation
     );
     if (!process_created) {
-        std::fprintf(stderr, "CreateProcessA failed, %d\n", GetLastError());
+        std::fprintf(stderr, "CreateProcessA failed with error %d\n", GetLastError());
         return false;
     }
 

From d57cd8bcddab1dadec76dc7274f63e8bdf30a32b Mon Sep 17 00:00:00 2001
From: lat9nq <lat9nq@gmail.com>
Date: Sun, 10 Jul 2022 17:18:31 -0400
Subject: [PATCH 5/7] startup_checks: Clean up

Adds some comments, removes unused includes, and removes last bits of
logging since this is before the logging backend starts up.
---
 src/yuzu/startup_checks.cpp | 15 ++++++---------
 1 file changed, 6 insertions(+), 9 deletions(-)

diff --git a/src/yuzu/startup_checks.cpp b/src/yuzu/startup_checks.cpp
index 6bd895400a..b27b9c8d93 100644
--- a/src/yuzu/startup_checks.cpp
+++ b/src/yuzu/startup_checks.cpp
@@ -14,17 +14,12 @@
 #endif
 
 #include <cstdio>
-#include <filesystem>
-#include <fstream>
-#include "common/fs/fs.h"
-#include "common/fs/path_util.h"
-#include "common/logging/log.h"
 #include "video_core/vulkan_common/vulkan_instance.h"
 #include "video_core/vulkan_common/vulkan_library.h"
 #include "yuzu/startup_checks.h"
-#include "yuzu/uisettings.h"
 
 void CheckVulkan() {
+    // Just start the Vulkan loader, this will crash if something is wrong
     try {
         Vulkan::vk::InstanceDispatch dld;
         const Common::DynamicLibrary library = Vulkan::OpenLibrary();
@@ -32,7 +27,7 @@ void CheckVulkan() {
             Vulkan::CreateInstance(library, dld, VK_API_VERSION_1_0);
 
     } catch (const Vulkan::vk::Exception& exception) {
-        LOG_ERROR(Frontend, "Failed to initialize Vulkan: {}", exception.what());
+        std::fprintf(stderr, "Failed to initialize Vulkan: %s\n", exception.what());
     }
 }
 
@@ -63,7 +58,7 @@ bool StartupChecks(const char* arg0, bool* has_broken_vulkan) {
         return false;
     }
 
-    // wait until the processs exits
+    // Wait until the processs exits and get exit code from it
     DWORD exit_code = STILL_ACTIVE;
     while (exit_code == STILL_ACTIVE) {
         const int err = GetExitCodeProcess(process_info.hProcess, &exit_code);
@@ -73,6 +68,7 @@ bool StartupChecks(const char* arg0, bool* has_broken_vulkan) {
         }
     }
 
+    // Vulkan is broken if the child crashed (return value is not zero)
     *has_broken_vulkan = (exit_code != 0);
 
     if (CloseHandle(process_info.hProcess) == 0) {
@@ -93,6 +89,7 @@ bool StartupChecks(const char* arg0, bool* has_broken_vulkan) {
         return false;
     }
 
+    // Get exit code from child process
     int status;
     const int r_val = wait(&status);
     if (r_val == -1) {
@@ -100,6 +97,7 @@ bool StartupChecks(const char* arg0, bool* has_broken_vulkan) {
         std::fprintf(stderr, "wait failed with error %d\n", err);
         return false;
     }
+    // Vulkan is broken if the child crashed (return value is not zero)
     *has_broken_vulkan = (status != 0);
 #endif
     return false;
@@ -115,7 +113,6 @@ bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi) {
     char p_name[255];
     std::strncpy(p_name, arg0, 255);
 
-    // TODO: use argv[0] instead of yuzu.exe
     const bool process_created = CreateProcessA(nullptr,       // lpApplicationName
                                                 p_name,        // lpCommandLine
                                                 nullptr,       // lpProcessAttributes

From 2d2a69ab5bec849c923b743186e827460221a4b4 Mon Sep 17 00:00:00 2001
From: lat9nq <lat9nq@gmail.com>
Date: Sun, 10 Jul 2022 20:24:10 -0400
Subject: [PATCH 6/7] startup_checks: Use GetEnvironmentVariableA

Solves MSVC compile error. Also drops need string use for comparison.
---
 src/yuzu/startup_checks.cpp | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/src/yuzu/startup_checks.cpp b/src/yuzu/startup_checks.cpp
index b27b9c8d93..0919d89c65 100644
--- a/src/yuzu/startup_checks.cpp
+++ b/src/yuzu/startup_checks.cpp
@@ -34,11 +34,10 @@ void CheckVulkan() {
 bool StartupChecks(const char* arg0, bool* has_broken_vulkan) {
 #ifdef _WIN32
     // Check environment variable to see if we are the child
-    char variable_contents[32];
+    char variable_contents[8];
     const DWORD startup_check_var =
-        GetEnvironmentVariable(STARTUP_CHECK_ENV_VAR, variable_contents, 32);
-    const std::string variable_contents_s{variable_contents};
-    if (startup_check_var > 0 && variable_contents_s == "ON") {
+        GetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, variable_contents, 8);
+    if (startup_check_var > 0 && std::strncmp(variable_contents, "ON", 8) == 0) {
         CheckVulkan();
         return true;
     }

From 7d9369d15ea6061e4b3a48cc8dbe442501a86ba1 Mon Sep 17 00:00:00 2001
From: lat9nq <lat9nq@gmail.com>
Date: Tue, 12 Jul 2022 14:23:50 -0400
Subject: [PATCH 7/7] startup_checks: Use WaitForSingleObject and more cleanup

---
 src/yuzu/startup_checks.cpp | 15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)

diff --git a/src/yuzu/startup_checks.cpp b/src/yuzu/startup_checks.cpp
index 0919d89c65..8421280bf2 100644
--- a/src/yuzu/startup_checks.cpp
+++ b/src/yuzu/startup_checks.cpp
@@ -58,13 +58,11 @@ bool StartupChecks(const char* arg0, bool* has_broken_vulkan) {
     }
 
     // Wait until the processs exits and get exit code from it
+    WaitForSingleObject(process_info.hProcess, INFINITE);
     DWORD exit_code = STILL_ACTIVE;
-    while (exit_code == STILL_ACTIVE) {
-        const int err = GetExitCodeProcess(process_info.hProcess, &exit_code);
-        if (err == 0) {
-            std::fprintf(stderr, "GetExitCodeProcess failed with error %d\n", GetLastError());
-            break;
-        }
+    const int err = GetExitCodeProcess(process_info.hProcess, &exit_code);
+    if (err == 0) {
+        std::fprintf(stderr, "GetExitCodeProcess failed with error %d\n", GetLastError());
     }
 
     // Vulkan is broken if the child crashed (return value is not zero)
@@ -77,6 +75,11 @@ bool StartupChecks(const char* arg0, bool* has_broken_vulkan) {
         std::fprintf(stderr, "CloseHandle failed with error %d\n", GetLastError());
     }
 
+    if (!SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, nullptr)) {
+        std::fprintf(stderr, "SetEnvironmentVariableA failed to clear %s with error %d\n",
+                     STARTUP_CHECK_ENV_VAR, GetLastError());
+    }
+
 #elif defined(YUZU_UNIX)
     const pid_t pid = fork();
     if (pid == 0) {