summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar bunnei2022-07-25 12:12:41 -0700
committerGravatar GitHub2022-07-25 12:12:41 -0700
commit1bcde9dd9879fc2677695e9283f2fe1d68c935b2 (patch)
tree8653a5c7a234ab22b606b4a765ff3c1cbb3e3d74
parentMerge pull request #8549 from liamwhite/kscheduler-sc (diff)
parentstartup_checks: Use WaitForSingleObject and more cleanup (diff)
downloadyuzu-1bcde9dd9879fc2677695e9283f2fe1d68c935b2.tar.gz
yuzu-1bcde9dd9879fc2677695e9283f2fe1d68c935b2.tar.xz
yuzu-1bcde9dd9879fc2677695e9283f2fe1d68c935b2.zip
Merge pull request #8564 from lat9nq/dinner-fork
yuzu: Streamline broken Vulkan handling
-rw-r--r--src/yuzu/CMakeLists.txt4
-rw-r--r--src/yuzu/check_vulkan.cpp53
-rw-r--r--src/yuzu/check_vulkan.h6
-rw-r--r--src/yuzu/configuration/config.cpp8
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp37
-rw-r--r--src/yuzu/configuration/configure_graphics.h2
-rw-r--r--src/yuzu/configuration/configure_graphics.ui9
-rw-r--r--src/yuzu/main.cpp29
-rw-r--r--src/yuzu/main.h2
-rw-r--r--src/yuzu/startup_checks.cpp136
-rw-r--r--src/yuzu/startup_checks.h17
-rw-r--r--src/yuzu/uisettings.h2
12 files changed, 181 insertions, 124 deletions
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index a11a3b908..57e0e7025 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -30,8 +30,6 @@ add_executable(yuzu
30 applets/qt_web_browser_scripts.h 30 applets/qt_web_browser_scripts.h
31 bootmanager.cpp 31 bootmanager.cpp
32 bootmanager.h 32 bootmanager.h
33 check_vulkan.cpp
34 check_vulkan.h
35 compatdb.ui 33 compatdb.ui
36 compatibility_list.cpp 34 compatibility_list.cpp
37 compatibility_list.h 35 compatibility_list.h
@@ -158,6 +156,8 @@ add_executable(yuzu
158 main.cpp 156 main.cpp
159 main.h 157 main.h
160 main.ui 158 main.ui
159 startup_checks.cpp
160 startup_checks.h
161 uisettings.cpp 161 uisettings.cpp
162 uisettings.h 162 uisettings.h
163 util/controller_navigation.cpp 163 util/controller_navigation.cpp
diff --git a/src/yuzu/check_vulkan.cpp b/src/yuzu/check_vulkan.cpp
deleted file mode 100644
index e6d66ab34..000000000
--- a/src/yuzu/check_vulkan.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "video_core/vulkan_common/vulkan_wrapper.h"
5
6#include <filesystem>
7#include <fstream>
8#include "common/fs/fs.h"
9#include "common/fs/path_util.h"
10#include "common/logging/log.h"
11#include "video_core/vulkan_common/vulkan_instance.h"
12#include "video_core/vulkan_common/vulkan_library.h"
13#include "yuzu/check_vulkan.h"
14#include "yuzu/uisettings.h"
15
16constexpr char TEMP_FILE_NAME[] = "vulkan_check";
17
18bool CheckVulkan() {
19 if (UISettings::values.has_broken_vulkan) {
20 return true;
21 }
22
23 LOG_DEBUG(Frontend, "Checking presence of Vulkan");
24
25 const auto fs_config_loc = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir);
26 const auto temp_file_loc = fs_config_loc / TEMP_FILE_NAME;
27
28 if (std::filesystem::exists(temp_file_loc)) {
29 LOG_WARNING(Frontend, "Detected recovery from previous failed Vulkan initialization");
30
31 UISettings::values.has_broken_vulkan = true;
32 std::filesystem::remove(temp_file_loc);
33 return false;
34 }
35
36 std::ofstream temp_file_handle(temp_file_loc);
37 temp_file_handle.close();
38
39 try {
40 Vulkan::vk::InstanceDispatch dld;
41 const Common::DynamicLibrary library = Vulkan::OpenLibrary();
42 const Vulkan::vk::Instance instance =
43 Vulkan::CreateInstance(library, dld, VK_API_VERSION_1_0);
44
45 } catch (const Vulkan::vk::Exception& exception) {
46 LOG_ERROR(Frontend, "Failed to initialize Vulkan: {}", exception.what());
47 // Don't set has_broken_vulkan to true here: we care when loading Vulkan crashes the
48 // application, not when we can handle it.
49 }
50
51 std::filesystem::remove(temp_file_loc);
52 return true;
53}
diff --git a/src/yuzu/check_vulkan.h b/src/yuzu/check_vulkan.h
deleted file mode 100644
index e4ea93582..000000000
--- a/src/yuzu/check_vulkan.h
+++ /dev/null
@@ -1,6 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6bool CheckVulkan();
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 1f76e86b9..c841843f0 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -688,12 +688,6 @@ void Config::ReadRendererValues() {
688 ReadGlobalSetting(Settings::values.bg_green); 688 ReadGlobalSetting(Settings::values.bg_green);
689 ReadGlobalSetting(Settings::values.bg_blue); 689 ReadGlobalSetting(Settings::values.bg_blue);
690 690
691 if (!global && UISettings::values.has_broken_vulkan &&
692 Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::Vulkan &&
693 !Settings::values.renderer_backend.UsingGlobal()) {
694 Settings::values.renderer_backend.SetGlobal(true);
695 }
696
697 if (global) { 691 if (global) {
698 ReadBasicSetting(Settings::values.renderer_debug); 692 ReadBasicSetting(Settings::values.renderer_debug);
699 ReadBasicSetting(Settings::values.renderer_shader_feedback); 693 ReadBasicSetting(Settings::values.renderer_shader_feedback);
@@ -813,7 +807,6 @@ void Config::ReadUIValues() {
813 ReadBasicSetting(UISettings::values.pause_when_in_background); 807 ReadBasicSetting(UISettings::values.pause_when_in_background);
814 ReadBasicSetting(UISettings::values.mute_when_in_background); 808 ReadBasicSetting(UISettings::values.mute_when_in_background);
815 ReadBasicSetting(UISettings::values.hide_mouse); 809 ReadBasicSetting(UISettings::values.hide_mouse);
816 ReadBasicSetting(UISettings::values.has_broken_vulkan);
817 ReadBasicSetting(UISettings::values.disable_web_applet); 810 ReadBasicSetting(UISettings::values.disable_web_applet);
818 811
819 qt_config->endGroup(); 812 qt_config->endGroup();
@@ -1367,7 +1360,6 @@ void Config::SaveUIValues() {
1367 WriteBasicSetting(UISettings::values.pause_when_in_background); 1360 WriteBasicSetting(UISettings::values.pause_when_in_background);
1368 WriteBasicSetting(UISettings::values.mute_when_in_background); 1361 WriteBasicSetting(UISettings::values.mute_when_in_background);
1369 WriteBasicSetting(UISettings::values.hide_mouse); 1362 WriteBasicSetting(UISettings::values.hide_mouse);
1370 WriteBasicSetting(UISettings::values.has_broken_vulkan);
1371 WriteBasicSetting(UISettings::values.disable_web_applet); 1363 WriteBasicSetting(UISettings::values.disable_web_applet);
1372 1364
1373 qt_config->endGroup(); 1365 qt_config->endGroup();
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index 85f34dc35..6b33c4535 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
58 UpdateBackgroundColorButton(new_bg_color); 58 UpdateBackgroundColorButton(new_bg_color);
59 }); 59 });
60 60
61 connect(ui->button_check_vulkan, &QAbstractButton::clicked, this, [this] { 61 ui->api->setEnabled(!UISettings::values.has_broken_vulkan);
62 UISettings::values.has_broken_vulkan = false; 62 ui->api_widget->setEnabled(!UISettings::values.has_broken_vulkan ||
63 63 Settings::IsConfiguringGlobal());
64 if (RetrieveVulkanDevices()) {
65 ui->api->setEnabled(true);
66 ui->button_check_vulkan->hide();
67
68 for (const auto& device : vulkan_devices) {
69 ui->device->addItem(device);
70 }
71 } else {
72 UISettings::values.has_broken_vulkan = true;
73 }
74 });
75
76 ui->api->setEnabled(!UISettings::values.has_broken_vulkan.GetValue());
77 ui->button_check_vulkan->setVisible(UISettings::values.has_broken_vulkan.GetValue());
78
79 ui->bg_label->setVisible(Settings::IsConfiguringGlobal()); 64 ui->bg_label->setVisible(Settings::IsConfiguringGlobal());
80 ui->bg_combobox->setVisible(!Settings::IsConfiguringGlobal()); 65 ui->bg_combobox->setVisible(!Settings::IsConfiguringGlobal());
81} 66}
@@ -315,7 +300,7 @@ void ConfigureGraphics::UpdateAPILayout() {
315 vulkan_device = Settings::values.vulkan_device.GetValue(true); 300 vulkan_device = Settings::values.vulkan_device.GetValue(true);
316 shader_backend = Settings::values.shader_backend.GetValue(true); 301 shader_backend = Settings::values.shader_backend.GetValue(true);
317 ui->device_widget->setEnabled(false); 302 ui->device_widget->setEnabled(false);
318 ui->backend_widget->setEnabled(UISettings::values.has_broken_vulkan.GetValue()); 303 ui->backend_widget->setEnabled(false);
319 } else { 304 } else {
320 vulkan_device = Settings::values.vulkan_device.GetValue(); 305 vulkan_device = Settings::values.vulkan_device.GetValue();
321 shader_backend = Settings::values.shader_backend.GetValue(); 306 shader_backend = Settings::values.shader_backend.GetValue();
@@ -337,9 +322,9 @@ void ConfigureGraphics::UpdateAPILayout() {
337 } 322 }
338} 323}
339 324
340bool ConfigureGraphics::RetrieveVulkanDevices() try { 325void ConfigureGraphics::RetrieveVulkanDevices() try {
341 if (UISettings::values.has_broken_vulkan) { 326 if (UISettings::values.has_broken_vulkan) {
342 return false; 327 return;
343 } 328 }
344 329
345 using namespace Vulkan; 330 using namespace Vulkan;
@@ -355,11 +340,8 @@ bool ConfigureGraphics::RetrieveVulkanDevices() try {
355 const std::string name = vk::PhysicalDevice(device, dld).GetProperties().deviceName; 340 const std::string name = vk::PhysicalDevice(device, dld).GetProperties().deviceName;
356 vulkan_devices.push_back(QString::fromStdString(name)); 341 vulkan_devices.push_back(QString::fromStdString(name));
357 } 342 }
358
359 return true;
360} catch (const Vulkan::vk::Exception& exception) { 343} catch (const Vulkan::vk::Exception& exception) {
361 LOG_ERROR(Frontend, "Failed to enumerate devices with error: {}", exception.what()); 344 LOG_ERROR(Frontend, "Failed to enumerate devices with error: {}", exception.what());
362 return false;
363} 345}
364 346
365Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const { 347Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const {
@@ -440,11 +422,4 @@ void ConfigureGraphics::SetupPerGameUI() {
440 ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true))); 422 ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true)));
441 ConfigurationShared::InsertGlobalItem( 423 ConfigurationShared::InsertGlobalItem(
442 ui->nvdec_emulation, static_cast<int>(Settings::values.nvdec_emulation.GetValue(true))); 424 ui->nvdec_emulation, static_cast<int>(Settings::values.nvdec_emulation.GetValue(true)));
443
444 if (UISettings::values.has_broken_vulkan) {
445 ui->backend_widget->setEnabled(true);
446 ConfigurationShared::SetColoredComboBox(
447 ui->backend, ui->backend_widget,
448 static_cast<int>(Settings::values.shader_backend.GetValue(true)));
449 }
450} 425}
diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h
index 8438f0187..1b101c940 100644
--- a/src/yuzu/configuration/configure_graphics.h
+++ b/src/yuzu/configuration/configure_graphics.h
@@ -41,7 +41,7 @@ private:
41 void UpdateDeviceSelection(int device); 41 void UpdateDeviceSelection(int device);
42 void UpdateShaderBackendSelection(int backend); 42 void UpdateShaderBackendSelection(int backend);
43 43
44 bool RetrieveVulkanDevices(); 44 void RetrieveVulkanDevices();
45 45
46 void SetupPerGameUI(); 46 void SetupPerGameUI();
47 47
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index 2f94c94bc..1e4f74704 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -6,7 +6,7 @@
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>471</width> 9 <width>541</width>
10 <height>759</height> 10 <height>759</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
@@ -574,13 +574,6 @@
574 </property> 574 </property>
575 </spacer> 575 </spacer>
576 </item> 576 </item>
577 <item>
578 <widget class="QPushButton" name="button_check_vulkan">
579 <property name="text">
580 <string>Check for Working Vulkan</string>
581 </property>
582 </widget>
583 </item>
584 </layout> 577 </layout>
585 </widget> 578 </widget>
586 <resources/> 579 <resources/>
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 08ccc1555..2814548eb 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -115,7 +115,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
115#include "video_core/shader_notify.h" 115#include "video_core/shader_notify.h"
116#include "yuzu/about_dialog.h" 116#include "yuzu/about_dialog.h"
117#include "yuzu/bootmanager.h" 117#include "yuzu/bootmanager.h"
118#include "yuzu/check_vulkan.h"
119#include "yuzu/compatdb.h" 118#include "yuzu/compatdb.h"
120#include "yuzu/compatibility_list.h" 119#include "yuzu/compatibility_list.h"
121#include "yuzu/configuration/config.h" 120#include "yuzu/configuration/config.h"
@@ -131,6 +130,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
131#include "yuzu/install_dialog.h" 130#include "yuzu/install_dialog.h"
132#include "yuzu/loading_screen.h" 131#include "yuzu/loading_screen.h"
133#include "yuzu/main.h" 132#include "yuzu/main.h"
133#include "yuzu/startup_checks.h"
134#include "yuzu/uisettings.h" 134#include "yuzu/uisettings.h"
135 135
136using namespace Common::Literals; 136using namespace Common::Literals;
@@ -252,7 +252,7 @@ static QString PrettyProductName() {
252 return QSysInfo::prettyProductName(); 252 return QSysInfo::prettyProductName();
253} 253}
254 254
255GMainWindow::GMainWindow() 255GMainWindow::GMainWindow(bool has_broken_vulkan)
256 : ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()}, 256 : ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()},
257 input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, 257 input_subsystem{std::make_shared<InputCommon::InputSubsystem>()},
258 config{std::make_unique<Config>(*system)}, 258 config{std::make_unique<Config>(*system)},
@@ -352,17 +352,15 @@ GMainWindow::GMainWindow()
352 352
353 MigrateConfigFiles(); 353 MigrateConfigFiles();
354 354
355 if (!CheckVulkan()) { 355 if (has_broken_vulkan) {
356 config->Save(); 356 UISettings::values.has_broken_vulkan = true;
357
358 QMessageBox::warning(this, tr("Broken Vulkan Installation Detected"),
359 tr("Vulkan initialization failed during boot.<br><br>Click <a "
360 "href='https://yuzu-emu.org/wiki/faq/"
361 "#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>"
362 "here for instructions to fix the issue</a>."));
357 363
358 QMessageBox::warning(
359 this, tr("Broken Vulkan Installation Detected"),
360 tr("Vulkan initialization failed on the previous boot.<br><br>Click <a "
361 "href='https://yuzu-emu.org/wiki/faq/"
362 "#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for "
363 "instructions to fix the issue</a>."));
364 }
365 if (UISettings::values.has_broken_vulkan) {
366 Settings::values.renderer_backend = Settings::RendererBackend::OpenGL; 364 Settings::values.renderer_backend = Settings::RendererBackend::OpenGL;
367 365
368 renderer_status_button->setDisabled(true); 366 renderer_status_button->setDisabled(true);
@@ -3879,6 +3877,11 @@ void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) {
3879#endif 3877#endif
3880 3878
3881int main(int argc, char* argv[]) { 3879int main(int argc, char* argv[]) {
3880 bool has_broken_vulkan = false;
3881 if (StartupChecks(argv[0], &has_broken_vulkan)) {
3882 return 0;
3883 }
3884
3882 Common::DetachedTasks detached_tasks; 3885 Common::DetachedTasks detached_tasks;
3883 MicroProfileOnThreadCreate("Frontend"); 3886 MicroProfileOnThreadCreate("Frontend");
3884 SCOPE_EXIT({ MicroProfileShutdown(); }); 3887 SCOPE_EXIT({ MicroProfileShutdown(); });
@@ -3918,7 +3921,7 @@ int main(int argc, char* argv[]) {
3918 // generating shaders 3921 // generating shaders
3919 setlocale(LC_ALL, "C"); 3922 setlocale(LC_ALL, "C");
3920 3923
3921 GMainWindow main_window{}; 3924 GMainWindow main_window{has_broken_vulkan};
3922 // After settings have been loaded by GMainWindow, apply the filter 3925 // After settings have been loaded by GMainWindow, apply the filter
3923 main_window.show(); 3926 main_window.show();
3924 3927
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 09e37f152..27204f5a2 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -118,7 +118,7 @@ class GMainWindow : public QMainWindow {
118public: 118public:
119 void filterBarSetChecked(bool state); 119 void filterBarSetChecked(bool state);
120 void UpdateUITheme(); 120 void UpdateUITheme();
121 explicit GMainWindow(); 121 explicit GMainWindow(bool has_broken_vulkan);
122 ~GMainWindow() override; 122 ~GMainWindow() override;
123 123
124 bool DropAction(QDropEvent* event); 124 bool DropAction(QDropEvent* event);
diff --git a/src/yuzu/startup_checks.cpp b/src/yuzu/startup_checks.cpp
new file mode 100644
index 000000000..8421280bf
--- /dev/null
+++ b/src/yuzu/startup_checks.cpp
@@ -0,0 +1,136 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "video_core/vulkan_common/vulkan_wrapper.h"
5
6#ifdef _WIN32
7#include <cstring> // for memset, strncpy
8#include <processthreadsapi.h>
9#include <windows.h>
10#elif defined(YUZU_UNIX)
11#include <errno.h>
12#include <sys/wait.h>
13#include <unistd.h>
14#endif
15
16#include <cstdio>
17#include "video_core/vulkan_common/vulkan_instance.h"
18#include "video_core/vulkan_common/vulkan_library.h"
19#include "yuzu/startup_checks.h"
20
21void CheckVulkan() {
22 // Just start the Vulkan loader, this will crash if something is wrong
23 try {
24 Vulkan::vk::InstanceDispatch dld;
25 const Common::DynamicLibrary library = Vulkan::OpenLibrary();
26 const Vulkan::vk::Instance instance =
27 Vulkan::CreateInstance(library, dld, VK_API_VERSION_1_0);
28
29 } catch (const Vulkan::vk::Exception& exception) {
30 std::fprintf(stderr, "Failed to initialize Vulkan: %s\n", exception.what());
31 }
32}
33
34bool StartupChecks(const char* arg0, bool* has_broken_vulkan) {
35#ifdef _WIN32
36 // Check environment variable to see if we are the child
37 char variable_contents[8];
38 const DWORD startup_check_var =
39 GetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, variable_contents, 8);
40 if (startup_check_var > 0 && std::strncmp(variable_contents, "ON", 8) == 0) {
41 CheckVulkan();
42 return true;
43 }
44
45 // Set the startup variable for child processes
46 const bool env_var_set = SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, "ON");
47 if (!env_var_set) {
48 std::fprintf(stderr, "SetEnvironmentVariableA failed to set %s with error %d\n",
49 STARTUP_CHECK_ENV_VAR, GetLastError());
50 return false;
51 }
52
53 PROCESS_INFORMATION process_info;
54 std::memset(&process_info, '\0', sizeof(process_info));
55
56 if (!SpawnChild(arg0, &process_info)) {
57 return false;
58 }
59
60 // Wait until the processs exits and get exit code from it
61 WaitForSingleObject(process_info.hProcess, INFINITE);
62 DWORD exit_code = STILL_ACTIVE;
63 const int err = GetExitCodeProcess(process_info.hProcess, &exit_code);
64 if (err == 0) {
65 std::fprintf(stderr, "GetExitCodeProcess failed with error %d\n", GetLastError());
66 }
67
68 // Vulkan is broken if the child crashed (return value is not zero)
69 *has_broken_vulkan = (exit_code != 0);
70
71 if (CloseHandle(process_info.hProcess) == 0) {
72 std::fprintf(stderr, "CloseHandle failed with error %d\n", GetLastError());
73 }
74 if (CloseHandle(process_info.hThread) == 0) {
75 std::fprintf(stderr, "CloseHandle failed with error %d\n", GetLastError());
76 }
77
78 if (!SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, nullptr)) {
79 std::fprintf(stderr, "SetEnvironmentVariableA failed to clear %s with error %d\n",
80 STARTUP_CHECK_ENV_VAR, GetLastError());
81 }
82
83#elif defined(YUZU_UNIX)
84 const pid_t pid = fork();
85 if (pid == 0) {
86 CheckVulkan();
87 return true;
88 } else if (pid == -1) {
89 const int err = errno;
90 std::fprintf(stderr, "fork failed with error %d\n", err);
91 return false;
92 }
93
94 // Get exit code from child process
95 int status;
96 const int r_val = wait(&status);
97 if (r_val == -1) {
98 const int err = errno;
99 std::fprintf(stderr, "wait failed with error %d\n", err);
100 return false;
101 }
102 // Vulkan is broken if the child crashed (return value is not zero)
103 *has_broken_vulkan = (status != 0);
104#endif
105 return false;
106}
107
108#ifdef _WIN32
109bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi) {
110 STARTUPINFOA startup_info;
111
112 std::memset(&startup_info, '\0', sizeof(startup_info));
113 startup_info.cb = sizeof(startup_info);
114
115 char p_name[255];
116 std::strncpy(p_name, arg0, 255);
117
118 const bool process_created = CreateProcessA(nullptr, // lpApplicationName
119 p_name, // lpCommandLine
120 nullptr, // lpProcessAttributes
121 nullptr, // lpThreadAttributes
122 false, // bInheritHandles
123 0, // dwCreationFlags
124 nullptr, // lpEnvironment
125 nullptr, // lpCurrentDirectory
126 &startup_info, // lpStartupInfo
127 pi // lpProcessInformation
128 );
129 if (!process_created) {
130 std::fprintf(stderr, "CreateProcessA failed with error %d\n", GetLastError());
131 return false;
132 }
133
134 return true;
135}
136#endif
diff --git a/src/yuzu/startup_checks.h b/src/yuzu/startup_checks.h
new file mode 100644
index 000000000..096dd54a8
--- /dev/null
+++ b/src/yuzu/startup_checks.h
@@ -0,0 +1,17 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#ifdef _WIN32
7#include <windows.h>
8#endif
9
10constexpr char STARTUP_CHECK_ENV_VAR[] = "YUZU_DO_STARTUP_CHECKS";
11
12void CheckVulkan();
13bool StartupChecks(const char* arg0, bool* has_broken_vulkan);
14
15#ifdef _WIN32
16bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi);
17#endif
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index 044d88ca6..2f6948243 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -78,7 +78,7 @@ struct Values {
78 Settings::Setting<bool> mute_when_in_background{false, "muteWhenInBackground"}; 78 Settings::Setting<bool> mute_when_in_background{false, "muteWhenInBackground"};
79 Settings::Setting<bool> hide_mouse{true, "hideInactiveMouse"}; 79 Settings::Setting<bool> hide_mouse{true, "hideInactiveMouse"};
80 // Set when Vulkan is known to crash the application 80 // Set when Vulkan is known to crash the application
81 Settings::Setting<bool> has_broken_vulkan{false, "has_broken_vulkan"}; 81 bool has_broken_vulkan = false;
82 82
83 Settings::Setting<bool> select_user_on_boot{false, "select_user_on_boot"}; 83 Settings::Setting<bool> select_user_on_boot{false, "select_user_on_boot"};
84 84