yuzu: Simplify broken Vulkan handling

This commit is contained in:
lat9nq 2022-07-10 16:10:35 -04:00
parent 4f15d9ed6f
commit 33abdfff9b
9 changed files with 65 additions and 115 deletions

View file

@ -682,12 +682,6 @@ void Config::ReadRendererValues() {
ReadGlobalSetting(Settings::values.bg_green); ReadGlobalSetting(Settings::values.bg_green);
ReadGlobalSetting(Settings::values.bg_blue); 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) { if (global) {
ReadBasicSetting(Settings::values.renderer_debug); ReadBasicSetting(Settings::values.renderer_debug);
ReadBasicSetting(Settings::values.renderer_shader_feedback); ReadBasicSetting(Settings::values.renderer_shader_feedback);
@ -807,7 +801,6 @@ void Config::ReadUIValues() {
ReadBasicSetting(UISettings::values.pause_when_in_background); ReadBasicSetting(UISettings::values.pause_when_in_background);
ReadBasicSetting(UISettings::values.mute_when_in_background); ReadBasicSetting(UISettings::values.mute_when_in_background);
ReadBasicSetting(UISettings::values.hide_mouse); ReadBasicSetting(UISettings::values.hide_mouse);
ReadBasicSetting(UISettings::values.has_broken_vulkan);
ReadBasicSetting(UISettings::values.disable_web_applet); ReadBasicSetting(UISettings::values.disable_web_applet);
qt_config->endGroup(); qt_config->endGroup();
@ -1355,7 +1348,6 @@ void Config::SaveUIValues() {
WriteBasicSetting(UISettings::values.pause_when_in_background); WriteBasicSetting(UISettings::values.pause_when_in_background);
WriteBasicSetting(UISettings::values.mute_when_in_background); WriteBasicSetting(UISettings::values.mute_when_in_background);
WriteBasicSetting(UISettings::values.hide_mouse); WriteBasicSetting(UISettings::values.hide_mouse);
WriteBasicSetting(UISettings::values.has_broken_vulkan);
WriteBasicSetting(UISettings::values.disable_web_applet); WriteBasicSetting(UISettings::values.disable_web_applet);
qt_config->endGroup(); qt_config->endGroup();

View file

@ -58,24 +58,9 @@ ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* paren
UpdateBackgroundColorButton(new_bg_color); UpdateBackgroundColorButton(new_bg_color);
}); });
connect(ui->button_check_vulkan, &QAbstractButton::clicked, this, [this] { ui->api->setEnabled(!UISettings::values.has_broken_vulkan);
UISettings::values.has_broken_vulkan = false; ui->api_widget->setEnabled(!UISettings::values.has_broken_vulkan ||
Settings::IsConfiguringGlobal());
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->bg_label->setVisible(Settings::IsConfiguringGlobal()); ui->bg_label->setVisible(Settings::IsConfiguringGlobal());
ui->bg_combobox->setVisible(!Settings::IsConfiguringGlobal()); ui->bg_combobox->setVisible(!Settings::IsConfiguringGlobal());
} }
@ -315,7 +300,7 @@ void ConfigureGraphics::UpdateAPILayout() {
vulkan_device = Settings::values.vulkan_device.GetValue(true); vulkan_device = Settings::values.vulkan_device.GetValue(true);
shader_backend = Settings::values.shader_backend.GetValue(true); shader_backend = Settings::values.shader_backend.GetValue(true);
ui->device_widget->setEnabled(false); ui->device_widget->setEnabled(false);
ui->backend_widget->setEnabled(UISettings::values.has_broken_vulkan.GetValue()); ui->backend_widget->setEnabled(false);
} else { } else {
vulkan_device = Settings::values.vulkan_device.GetValue(); vulkan_device = Settings::values.vulkan_device.GetValue();
shader_backend = Settings::values.shader_backend.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) { if (UISettings::values.has_broken_vulkan) {
return false; return;
} }
using namespace Vulkan; using namespace Vulkan;
@ -355,11 +340,8 @@ bool ConfigureGraphics::RetrieveVulkanDevices() try {
const std::string name = vk::PhysicalDevice(device, dld).GetProperties().deviceName; const std::string name = vk::PhysicalDevice(device, dld).GetProperties().deviceName;
vulkan_devices.push_back(QString::fromStdString(name)); vulkan_devices.push_back(QString::fromStdString(name));
} }
return true;
} catch (const Vulkan::vk::Exception& exception) { } catch (const Vulkan::vk::Exception& exception) {
LOG_ERROR(Frontend, "Failed to enumerate devices with error: {}", exception.what()); LOG_ERROR(Frontend, "Failed to enumerate devices with error: {}", exception.what());
return false;
} }
Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const { Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const {
@ -440,11 +422,4 @@ void ConfigureGraphics::SetupPerGameUI() {
ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true))); ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true)));
ConfigurationShared::InsertGlobalItem( ConfigurationShared::InsertGlobalItem(
ui->nvdec_emulation, static_cast<int>(Settings::values.nvdec_emulation.GetValue(true))); 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)));
}
} }

View file

@ -41,7 +41,7 @@ private:
void UpdateDeviceSelection(int device); void UpdateDeviceSelection(int device);
void UpdateShaderBackendSelection(int backend); void UpdateShaderBackendSelection(int backend);
bool RetrieveVulkanDevices(); void RetrieveVulkanDevices();
void SetupPerGameUI(); void SetupPerGameUI();

View file

@ -6,7 +6,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>471</width> <width>541</width>
<height>759</height> <height>759</height>
</rect> </rect>
</property> </property>
@ -574,13 +574,6 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item>
<widget class="QPushButton" name="button_check_vulkan">
<property name="text">
<string>Check for Working Vulkan</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<resources/> <resources/>

View file

@ -252,7 +252,7 @@ static QString PrettyProductName() {
return QSysInfo::prettyProductName(); return QSysInfo::prettyProductName();
} }
GMainWindow::GMainWindow() GMainWindow::GMainWindow(bool has_broken_vulkan)
: ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()}, : ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()},
input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, input_subsystem{std::make_shared<InputCommon::InputSubsystem>()},
config{std::make_unique<Config>(*system)}, config{std::make_unique<Config>(*system)},
@ -352,17 +352,15 @@ GMainWindow::GMainWindow()
MigrateConfigFiles(); MigrateConfigFiles();
if (!CheckVulkan()) { if (has_broken_vulkan) {
config->Save(); 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; Settings::values.renderer_backend = Settings::RendererBackend::OpenGL;
renderer_status_button->setDisabled(true); renderer_status_button->setDisabled(true);
@ -3853,17 +3851,17 @@ void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) {
#endif #endif
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
bool has_broken_vulkan = false;
#ifdef _WIN32 #ifdef _WIN32
char variable_contents[32]; char variable_contents[32];
const DWORD startup_check_var = const DWORD startup_check_var =
GetEnvironmentVariable(STARTUP_CHECK_ENV_VAR, variable_contents, 32); GetEnvironmentVariable(STARTUP_CHECK_ENV_VAR, variable_contents, 32);
if (startup_check_var != 0) { const std::string variable_contents_s{variable_contents};
std::fprintf(stderr, "perform statup checks\n"); if (startup_check_var > 0 && variable_contents_s == "ON") {
CheckVulkan(); CheckVulkan();
return 0; return 0;
} else {
std::fprintf(stderr, "%d\n", StartupChecks());
} }
StartupChecks(argv[0], &has_broken_vulkan);
#elif YUZU_UNIX #elif YUZU_UNIX
#error "Unimplemented" #error "Unimplemented"
#endif #endif
@ -3907,7 +3905,7 @@ int main(int argc, char* argv[]) {
// generating shaders // generating shaders
setlocale(LC_ALL, "C"); setlocale(LC_ALL, "C");
GMainWindow main_window{}; GMainWindow main_window{has_broken_vulkan};
// After settings have been loaded by GMainWindow, apply the filter // After settings have been loaded by GMainWindow, apply the filter
main_window.show(); main_window.show();

View file

@ -118,7 +118,7 @@ class GMainWindow : public QMainWindow {
public: public:
void filterBarSetChecked(bool state); void filterBarSetChecked(bool state);
void UpdateUITheme(); void UpdateUITheme();
explicit GMainWindow(); explicit GMainWindow(bool has_broken_vulkan);
~GMainWindow() override; ~GMainWindow() override;
bool DropAction(QDropEvent* event); bool DropAction(QDropEvent* event);

View file

@ -22,29 +22,7 @@
#include "yuzu/startup_checks.h" #include "yuzu/startup_checks.h"
#include "yuzu/uisettings.h" #include "yuzu/uisettings.h"
constexpr char TEMP_FILE_NAME[] = "vulkan_check"; void CheckVulkan() {
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();
try { try {
Vulkan::vk::InstanceDispatch dld; Vulkan::vk::InstanceDispatch dld;
const Common::DynamicLibrary library = Vulkan::OpenLibrary(); const Common::DynamicLibrary library = Vulkan::OpenLibrary();
@ -53,32 +31,48 @@ bool CheckVulkan() {
} catch (const Vulkan::vk::Exception& exception) { } catch (const Vulkan::vk::Exception& exception) {
LOG_ERROR(Frontend, "Failed to initialize Vulkan: {}", exception.what()); 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 #ifdef _WIN32
const bool env_var_set = SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, "ON"); const bool env_var_set = SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, "ON");
if (!env_var_set) { if (!env_var_set) {
LOG_ERROR(Frontend, "SetEnvironmentVariableA failed to set {}, {}", STARTUP_CHECK_ENV_VAR, std::fprintf(stderr, "SetEnvironmentVariableA failed to set %s, %d\n",
GetLastError()); STARTUP_CHECK_ENV_VAR, GetLastError());
return false; return false;
} }
STARTUPINFOA startup_info;
PROCESS_INFORMATION process_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(&startup_info, '\0', sizeof(startup_info));
std::memset(&process_info, '\0', sizeof(process_info));
startup_info.cb = sizeof(startup_info); startup_info.cb = sizeof(startup_info);
char p_name[255]; 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 // TODO: use argv[0] instead of yuzu.exe
const bool process_created = CreateProcessA(nullptr, // lpApplicationName const bool process_created = CreateProcessA(nullptr, // lpApplicationName
@ -90,23 +84,13 @@ bool StartupChecks() {
nullptr, // lpEnvironment nullptr, // lpEnvironment
nullptr, // lpCurrentDirectory nullptr, // lpCurrentDirectory
&startup_info, // lpStartupInfo &startup_info, // lpStartupInfo
&process_info // lpProcessInformation pi // lpProcessInformation
); );
if (!process_created) { if (!process_created) {
LOG_ERROR(Frontend, "CreateProcessA failed, {}", GetLastError()); std::fprintf(stderr, "CreateProcessA failed, %d\n", GetLastError());
return false; 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; return true;
} }
#endif

View file

@ -3,7 +3,15 @@
#pragma once #pragma once
#ifdef _WIN32
#include <windows.h>
#endif
constexpr char STARTUP_CHECK_ENV_VAR[] = "YUZU_DO_STARTUP_CHECKS"; constexpr char STARTUP_CHECK_ENV_VAR[] = "YUZU_DO_STARTUP_CHECKS";
bool CheckVulkan(); void CheckVulkan();
bool StartupChecks(); bool StartupChecks(const char* arg0, bool* has_broken_vulkan);
#ifdef _WIN32
bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi);
#endif

View file

@ -78,7 +78,7 @@ struct Values {
Settings::Setting<bool> mute_when_in_background{false, "muteWhenInBackground"}; Settings::Setting<bool> mute_when_in_background{false, "muteWhenInBackground"};
Settings::Setting<bool> hide_mouse{true, "hideInactiveMouse"}; Settings::Setting<bool> hide_mouse{true, "hideInactiveMouse"};
// Set when Vulkan is known to crash the application // 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"}; Settings::Setting<bool> select_user_on_boot{false, "select_user_on_boot"};