startup_checks.cpp 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "video_core/vulkan_common/vulkan_wrapper.h"
  4. #ifdef _WIN32
  5. #include <cstring> // for memset, strncpy
  6. #include <processthreadsapi.h>
  7. #include <windows.h>
  8. #elif defined(YUZU_UNIX)
  9. #include <errno.h>
  10. #include <sys/wait.h>
  11. #include <unistd.h>
  12. #endif
  13. #include <cstdio>
  14. #include <filesystem>
  15. #include <fstream>
  16. #include "common/fs/fs.h"
  17. #include "common/fs/path_util.h"
  18. #include "common/logging/log.h"
  19. #include "video_core/vulkan_common/vulkan_instance.h"
  20. #include "video_core/vulkan_common/vulkan_library.h"
  21. #include "yuzu/startup_checks.h"
  22. #include "yuzu/uisettings.h"
  23. void CheckVulkan() {
  24. try {
  25. Vulkan::vk::InstanceDispatch dld;
  26. const Common::DynamicLibrary library = Vulkan::OpenLibrary();
  27. const Vulkan::vk::Instance instance =
  28. Vulkan::CreateInstance(library, dld, VK_API_VERSION_1_0);
  29. } catch (const Vulkan::vk::Exception& exception) {
  30. LOG_ERROR(Frontend, "Failed to initialize Vulkan: {}", exception.what());
  31. }
  32. }
  33. bool StartupChecks(const char* arg0, bool* has_broken_vulkan) {
  34. #ifdef _WIN32
  35. // Check environment variable to see if we are the child
  36. char variable_contents[32];
  37. const DWORD startup_check_var =
  38. GetEnvironmentVariable(STARTUP_CHECK_ENV_VAR, variable_contents, 32);
  39. const std::string variable_contents_s{variable_contents};
  40. if (startup_check_var > 0 && variable_contents_s == "ON") {
  41. CheckVulkan();
  42. return true;
  43. }
  44. // Set the startup variable for child processes
  45. const bool env_var_set = SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, "ON");
  46. if (!env_var_set) {
  47. std::fprintf(stderr, "SetEnvironmentVariableA failed to set %s with error %d\n",
  48. STARTUP_CHECK_ENV_VAR, GetLastError());
  49. return false;
  50. }
  51. PROCESS_INFORMATION process_info;
  52. std::memset(&process_info, '\0', sizeof(process_info));
  53. if (!SpawnChild(arg0, &process_info)) {
  54. return false;
  55. }
  56. // wait until the processs exits
  57. DWORD exit_code = STILL_ACTIVE;
  58. while (exit_code == STILL_ACTIVE) {
  59. const int err = GetExitCodeProcess(process_info.hProcess, &exit_code);
  60. if (err == 0) {
  61. std::fprintf(stderr, "GetExitCodeProcess failed with error %d\n", GetLastError());
  62. break;
  63. }
  64. }
  65. *has_broken_vulkan = (exit_code != 0);
  66. if (CloseHandle(process_info.hProcess) == 0) {
  67. std::fprintf(stderr, "CloseHandle failed with error %d\n", GetLastError());
  68. }
  69. if (CloseHandle(process_info.hThread) == 0) {
  70. std::fprintf(stderr, "CloseHandle failed with error %d\n", GetLastError());
  71. }
  72. #elif defined(YUZU_UNIX)
  73. const pid_t pid = fork();
  74. if (pid == 0) {
  75. CheckVulkan();
  76. return true;
  77. } else if (pid == -1) {
  78. const int err = errno;
  79. std::fprintf(stderr, "fork failed with error %d\n", err);
  80. return false;
  81. }
  82. int status;
  83. const int r_val = wait(&status);
  84. if (r_val == -1) {
  85. const int err = errno;
  86. std::fprintf(stderr, "wait failed with error %d\n", err);
  87. return false;
  88. }
  89. *has_broken_vulkan = (status != 0);
  90. #endif
  91. return false;
  92. }
  93. #ifdef _WIN32
  94. bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi) {
  95. STARTUPINFOA startup_info;
  96. std::memset(&startup_info, '\0', sizeof(startup_info));
  97. startup_info.cb = sizeof(startup_info);
  98. char p_name[255];
  99. std::strncpy(p_name, arg0, 255);
  100. // TODO: use argv[0] instead of yuzu.exe
  101. const bool process_created = CreateProcessA(nullptr, // lpApplicationName
  102. p_name, // lpCommandLine
  103. nullptr, // lpProcessAttributes
  104. nullptr, // lpThreadAttributes
  105. false, // bInheritHandles
  106. 0, // dwCreationFlags
  107. nullptr, // lpEnvironment
  108. nullptr, // lpCurrentDirectory
  109. &startup_info, // lpStartupInfo
  110. pi // lpProcessInformation
  111. );
  112. if (!process_created) {
  113. std::fprintf(stderr, "CreateProcessA failed with error %d\n", GetLastError());
  114. return false;
  115. }
  116. return true;
  117. }
  118. #endif