From 568a0ac3974d837d7114e4480691b1d717451be2 Mon Sep 17 00:00:00 2001 From: lat9nq Date: Sun, 10 Jul 2022 11:46:53 -0400 Subject: yuzu: Rename check_vulkan to startup_checks --- src/yuzu/startup_checks.cpp | 53 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 src/yuzu/startup_checks.cpp (limited to 'src/yuzu/startup_checks.cpp') diff --git a/src/yuzu/startup_checks.cpp b/src/yuzu/startup_checks.cpp new file mode 100644 index 000000000..e6d66ab34 --- /dev/null +++ b/src/yuzu/startup_checks.cpp @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "video_core/vulkan_common/vulkan_wrapper.h" + +#include +#include +#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/check_vulkan.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(); + + try { + Vulkan::vk::InstanceDispatch dld; + const Common::DynamicLibrary library = Vulkan::OpenLibrary(); + const Vulkan::vk::Instance instance = + Vulkan::CreateInstance(library, dld, VK_API_VERSION_1_0); + + } 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; +} -- cgit v1.2.3 From 4f15d9ed6fba4fa1804c5be3f9378e3ad3d32688 Mon Sep 17 00:00:00 2001 From: lat9nq Date: Sun, 10 Jul 2022 14:08:20 -0400 Subject: yuzu: Check Vulkan on startup with a child --- src/yuzu/startup_checks.cpp | 61 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) (limited to 'src/yuzu/startup_checks.cpp') diff --git a/src/yuzu/startup_checks.cpp b/src/yuzu/startup_checks.cpp index e6d66ab34..cfd14a6d5 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 // for memset, strncpy +#include +#include +#elif defined(YUZU_UNIX) +#include +#endif + +#include #include #include #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; +} -- cgit v1.2.3 From 33abdfff9bf0a549b0d1d327e914e9a1ab4b799b Mon Sep 17 00:00:00 2001 From: lat9nq Date: Sun, 10 Jul 2022 16:10:35 -0400 Subject: yuzu: Simplify broken Vulkan handling --- src/yuzu/startup_checks.cpp | 78 ++++++++++++++++++--------------------------- 1 file changed, 31 insertions(+), 47 deletions(-) (limited to 'src/yuzu/startup_checks.cpp') diff --git a/src/yuzu/startup_checks.cpp b/src/yuzu/startup_checks.cpp index cfd14a6d5..c860f0aa0 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 -- cgit v1.2.3 From fd4e48f96e8328bbace2d4df4f2a6f7ab9a699f1 Mon Sep 17 00:00:00 2001 From: lat9nq Date: Sun, 10 Jul 2022 17:01:37 -0400 Subject: startup_checks: Implement unix side code Wow fork() is nice, isn't it? --- src/yuzu/startup_checks.cpp | 53 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 6 deletions(-) (limited to 'src/yuzu/startup_checks.cpp') diff --git a/src/yuzu/startup_checks.cpp b/src/yuzu/startup_checks.cpp index c860f0aa0..6bd895400 100644 --- a/src/yuzu/startup_checks.cpp +++ b/src/yuzu/startup_checks.cpp @@ -8,6 +8,8 @@ #include #include #elif defined(YUZU_UNIX) +#include +#include #include #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; } -- cgit v1.2.3 From d57cd8bcddab1dadec76dc7274f63e8bdf30a32b Mon Sep 17 00:00:00 2001 From: lat9nq Date: Sun, 10 Jul 2022 17:18:31 -0400 Subject: 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(-) (limited to 'src/yuzu/startup_checks.cpp') diff --git a/src/yuzu/startup_checks.cpp b/src/yuzu/startup_checks.cpp index 6bd895400..b27b9c8d9 100644 --- a/src/yuzu/startup_checks.cpp +++ b/src/yuzu/startup_checks.cpp @@ -14,17 +14,12 @@ #endif #include -#include -#include -#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 -- cgit v1.2.3 From 2d2a69ab5bec849c923b743186e827460221a4b4 Mon Sep 17 00:00:00 2001 From: lat9nq Date: Sun, 10 Jul 2022 20:24:10 -0400 Subject: 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(-) (limited to 'src/yuzu/startup_checks.cpp') diff --git a/src/yuzu/startup_checks.cpp b/src/yuzu/startup_checks.cpp index b27b9c8d9..0919d89c6 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; } -- cgit v1.2.3 From 7d9369d15ea6061e4b3a48cc8dbe442501a86ba1 Mon Sep 17 00:00:00 2001 From: lat9nq Date: Tue, 12 Jul 2022 14:23:50 -0400 Subject: startup_checks: Use WaitForSingleObject and more cleanup --- src/yuzu/startup_checks.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'src/yuzu/startup_checks.cpp') diff --git a/src/yuzu/startup_checks.cpp b/src/yuzu/startup_checks.cpp index 0919d89c6..8421280bf 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) { -- cgit v1.2.3