diff options
| author | 2022-07-25 12:12:41 -0700 | |
|---|---|---|
| committer | 2022-07-25 12:12:41 -0700 | |
| commit | 1bcde9dd9879fc2677695e9283f2fe1d68c935b2 (patch) | |
| tree | 8653a5c7a234ab22b606b4a765ff3c1cbb3e3d74 | |
| parent | Merge pull request #8549 from liamwhite/kscheduler-sc (diff) | |
| parent | startup_checks: Use WaitForSingleObject and more cleanup (diff) | |
| download | yuzu-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.txt | 4 | ||||
| -rw-r--r-- | src/yuzu/check_vulkan.cpp | 53 | ||||
| -rw-r--r-- | src/yuzu/check_vulkan.h | 6 | ||||
| -rw-r--r-- | src/yuzu/configuration/config.cpp | 8 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_graphics.cpp | 37 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_graphics.h | 2 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_graphics.ui | 9 | ||||
| -rw-r--r-- | src/yuzu/main.cpp | 29 | ||||
| -rw-r--r-- | src/yuzu/main.h | 2 | ||||
| -rw-r--r-- | src/yuzu/startup_checks.cpp | 136 | ||||
| -rw-r--r-- | src/yuzu/startup_checks.h | 17 | ||||
| -rw-r--r-- | src/yuzu/uisettings.h | 2 |
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 | |||
| 16 | constexpr char TEMP_FILE_NAME[] = "vulkan_check"; | ||
| 17 | |||
| 18 | bool 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 | |||
| 6 | bool 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 | ||
| 340 | bool ConfigureGraphics::RetrieveVulkanDevices() try { | 325 | void 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 | ||
| 365 | Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const { | 347 | Settings::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 | ||
| 136 | using namespace Common::Literals; | 136 | using namespace Common::Literals; |
| @@ -252,7 +252,7 @@ static QString PrettyProductName() { | |||
| 252 | return QSysInfo::prettyProductName(); | 252 | return QSysInfo::prettyProductName(); |
| 253 | } | 253 | } |
| 254 | 254 | ||
| 255 | GMainWindow::GMainWindow() | 255 | GMainWindow::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 | ||
| 3881 | int main(int argc, char* argv[]) { | 3879 | int 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 { | |||
| 118 | public: | 118 | public: |
| 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 | |||
| 21 | void 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 | |||
| 34 | bool 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 | ||
| 109 | bool 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 | |||
| 10 | constexpr char STARTUP_CHECK_ENV_VAR[] = "YUZU_DO_STARTUP_CHECKS"; | ||
| 11 | |||
| 12 | void CheckVulkan(); | ||
| 13 | bool StartupChecks(const char* arg0, bool* has_broken_vulkan); | ||
| 14 | |||
| 15 | #ifdef _WIN32 | ||
| 16 | bool 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 | ||