| |
| |
|
|
| #include "video_core/vulkan_common/vulkan_wrapper.h" |
|
|
| #ifdef _WIN32 |
| #include <cstring> |
| #include <processthreadsapi.h> |
| #include <windows.h> |
| #elif defined(YUZU_UNIX) |
| #include <cstring> |
| #include <errno.h> |
| #include <spawn.h> |
| #include <sys/types.h> |
| #include <sys/wait.h> |
| #include <unistd.h> |
| #endif |
|
|
| #include <fmt/core.h> |
| #include "video_core/vulkan_common/vulkan_instance.h" |
| #include "video_core/vulkan_common/vulkan_library.h" |
| #include "yuzu/startup_checks.h" |
|
|
| void CheckVulkan() { |
| |
| try { |
| Vulkan::vk::InstanceDispatch dld; |
| const auto library = Vulkan::OpenLibrary(); |
| const Vulkan::vk::Instance instance = |
| Vulkan::CreateInstance(*library, dld, VK_API_VERSION_1_1); |
|
|
| } catch (const Vulkan::vk::Exception& exception) { |
| fmt::print(stderr, "Failed to initialize Vulkan: {}\n", exception.what()); |
| } |
| } |
|
|
| bool CheckEnvVars(bool* is_child) { |
| #ifdef _WIN32 |
| |
| char variable_contents[8]; |
| const DWORD startup_check_var = |
| GetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, variable_contents, 8); |
| if (startup_check_var > 0 && std::strncmp(variable_contents, ENV_VAR_ENABLED_TEXT, 8) == 0) { |
| CheckVulkan(); |
| return true; |
| } |
|
|
| |
| char is_child_s[8]; |
| const DWORD is_child_len = GetEnvironmentVariableA(IS_CHILD_ENV_VAR, is_child_s, 8); |
| if (is_child_len > 0 && std::strncmp(is_child_s, ENV_VAR_ENABLED_TEXT, 8) == 0) { |
| *is_child = true; |
| return false; |
| } else if (!SetEnvironmentVariableA(IS_CHILD_ENV_VAR, ENV_VAR_ENABLED_TEXT)) { |
| fmt::print(stderr, "SetEnvironmentVariableA failed to set {} with error {}\n", |
| IS_CHILD_ENV_VAR, GetLastError()); |
| return true; |
| } |
| #elif defined(YUZU_UNIX) |
| const char* startup_check_var = getenv(STARTUP_CHECK_ENV_VAR); |
| if (startup_check_var != nullptr && |
| std::strncmp(startup_check_var, ENV_VAR_ENABLED_TEXT, 8) == 0) { |
| CheckVulkan(); |
| return true; |
| } |
| #endif |
| return false; |
| } |
|
|
| bool StartupChecks(const char* arg0, bool* has_broken_vulkan, bool perform_vulkan_check) { |
| #ifdef _WIN32 |
| |
| const bool env_var_set = SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, ENV_VAR_ENABLED_TEXT); |
| if (!env_var_set) { |
| fmt::print(stderr, "SetEnvironmentVariableA failed to set {} with error {}\n", |
| STARTUP_CHECK_ENV_VAR, GetLastError()); |
| return false; |
| } |
|
|
| if (perform_vulkan_check) { |
| |
| PROCESS_INFORMATION process_info; |
| std::memset(&process_info, '\0', sizeof(process_info)); |
|
|
| if (!SpawnChild(arg0, &process_info, 0)) { |
| return false; |
| } |
|
|
| |
| WaitForSingleObject(process_info.hProcess, INFINITE); |
| DWORD exit_code = STILL_ACTIVE; |
| const int err = GetExitCodeProcess(process_info.hProcess, &exit_code); |
| if (err == 0) { |
| fmt::print(stderr, "GetExitCodeProcess failed with error {}\n", GetLastError()); |
| } |
|
|
| |
| *has_broken_vulkan = (exit_code != 0); |
|
|
| if (CloseHandle(process_info.hProcess) == 0) { |
| fmt::print(stderr, "CloseHandle failed with error {}\n", GetLastError()); |
| } |
| if (CloseHandle(process_info.hThread) == 0) { |
| fmt::print(stderr, "CloseHandle failed with error {}\n", GetLastError()); |
| } |
| } |
|
|
| if (!SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, nullptr)) { |
| fmt::print(stderr, "SetEnvironmentVariableA failed to clear {} with error {}\n", |
| STARTUP_CHECK_ENV_VAR, GetLastError()); |
| } |
|
|
| #elif defined(YUZU_UNIX) |
| const int env_var_set = setenv(STARTUP_CHECK_ENV_VAR, ENV_VAR_ENABLED_TEXT, 1); |
| if (env_var_set == -1) { |
| const int err = errno; |
| fmt::print(stderr, "setenv failed to set {} with error {}\n", STARTUP_CHECK_ENV_VAR, err); |
| return false; |
| } |
|
|
| if (perform_vulkan_check) { |
| const pid_t pid = SpawnChild(arg0); |
| if (pid == -1) { |
| return false; |
| } |
|
|
| |
| int status; |
| const int r_val = waitpid(pid, &status, 0); |
| if (r_val == -1) { |
| const int err = errno; |
| fmt::print(stderr, "wait failed with error {}\n", err); |
| return false; |
| } |
| |
| *has_broken_vulkan = (status != 0); |
| } |
|
|
| const int env_var_cleared = unsetenv(STARTUP_CHECK_ENV_VAR); |
| if (env_var_cleared == -1) { |
| const int err = errno; |
| fmt::print(stderr, "unsetenv failed to clear {} with error {}\n", STARTUP_CHECK_ENV_VAR, |
| err); |
| } |
| #endif |
| return false; |
| } |
|
|
| #ifdef _WIN32 |
| bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi, int flags) { |
| STARTUPINFOA startup_info; |
|
|
| std::memset(&startup_info, '\0', sizeof(startup_info)); |
| startup_info.cb = sizeof(startup_info); |
|
|
| char p_name[255]; |
| std::strncpy(p_name, arg0, 254); |
| p_name[254] = '\0'; |
|
|
| const bool process_created = CreateProcessA(nullptr, |
| p_name, |
| nullptr, |
| nullptr, |
| false, |
| flags, |
| nullptr, |
| nullptr, |
| &startup_info, |
| pi |
| ); |
| if (!process_created) { |
| fmt::print(stderr, "CreateProcessA failed with error {}\n", GetLastError()); |
| return false; |
| } |
|
|
| return true; |
| } |
| #elif defined(YUZU_UNIX) |
| pid_t SpawnChild(const char* arg0) { |
| const pid_t pid = fork(); |
|
|
| if (pid == -1) { |
| |
| const int err = errno; |
| fmt::print(stderr, "fork failed with error {}\n", err); |
| return pid; |
| } else if (pid == 0) { |
| |
| execlp(arg0, arg0, nullptr); |
| const int err = errno; |
| fmt::print(stderr, "execl failed with error {}\n", err); |
| _exit(0); |
| } |
|
|
| return pid; |
| } |
| #endif |
|
|