startup_checks.cpp 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  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 "video_core/vulkan_common/vulkan_instance.h"
  15. #include "video_core/vulkan_common/vulkan_library.h"
  16. #include "yuzu/startup_checks.h"
  17. void CheckVulkan() {
  18. // Just start the Vulkan loader, this will crash if something is wrong
  19. try {
  20. Vulkan::vk::InstanceDispatch dld;
  21. const Common::DynamicLibrary library = Vulkan::OpenLibrary();
  22. const Vulkan::vk::Instance instance =
  23. Vulkan::CreateInstance(library, dld, VK_API_VERSION_1_0);
  24. } catch (const Vulkan::vk::Exception& exception) {
  25. std::fprintf(stderr, "Failed to initialize Vulkan: %s\n", exception.what());
  26. }
  27. }
  28. bool StartupChecks(const char* arg0, bool* has_broken_vulkan) {
  29. #ifdef _WIN32
  30. // Check environment variable to see if we are the child
  31. char variable_contents[8];
  32. const DWORD startup_check_var =
  33. GetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, variable_contents, 8);
  34. if (startup_check_var > 0 && std::strncmp(variable_contents, "ON", 8) == 0) {
  35. CheckVulkan();
  36. return true;
  37. }
  38. // Set the startup variable for child processes
  39. const bool env_var_set = SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, "ON");
  40. if (!env_var_set) {
  41. std::fprintf(stderr, "SetEnvironmentVariableA failed to set %s with error %d\n",
  42. STARTUP_CHECK_ENV_VAR, GetLastError());
  43. return false;
  44. }
  45. PROCESS_INFORMATION process_info;
  46. std::memset(&process_info, '\0', sizeof(process_info));
  47. if (!SpawnChild(arg0, &process_info)) {
  48. return false;
  49. }
  50. // Wait until the processs exits and get exit code from it
  51. WaitForSingleObject(process_info.hProcess, INFINITE);
  52. DWORD exit_code = STILL_ACTIVE;
  53. const int err = GetExitCodeProcess(process_info.hProcess, &exit_code);
  54. if (err == 0) {
  55. std::fprintf(stderr, "GetExitCodeProcess failed with error %d\n", GetLastError());
  56. }
  57. // Vulkan is broken if the child crashed (return value is not zero)
  58. *has_broken_vulkan = (exit_code != 0);
  59. if (CloseHandle(process_info.hProcess) == 0) {
  60. std::fprintf(stderr, "CloseHandle failed with error %d\n", GetLastError());
  61. }
  62. if (CloseHandle(process_info.hThread) == 0) {
  63. std::fprintf(stderr, "CloseHandle failed with error %d\n", GetLastError());
  64. }
  65. if (!SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, nullptr)) {
  66. std::fprintf(stderr, "SetEnvironmentVariableA failed to clear %s with error %d\n",
  67. STARTUP_CHECK_ENV_VAR, GetLastError());
  68. }
  69. #elif defined(YUZU_UNIX)
  70. const pid_t pid = fork();
  71. if (pid == 0) {
  72. CheckVulkan();
  73. return true;
  74. } else if (pid == -1) {
  75. const int err = errno;
  76. std::fprintf(stderr, "fork failed with error %d\n", err);
  77. return false;
  78. }
  79. // Get exit code from child process
  80. int status;
  81. const int r_val = wait(&status);
  82. if (r_val == -1) {
  83. const int err = errno;
  84. std::fprintf(stderr, "wait failed with error %d\n", err);
  85. return false;
  86. }
  87. // Vulkan is broken if the child crashed (return value is not zero)
  88. *has_broken_vulkan = (status != 0);
  89. #endif
  90. return false;
  91. }
  92. #ifdef _WIN32
  93. bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi) {
  94. STARTUPINFOA startup_info;
  95. std::memset(&startup_info, '\0', sizeof(startup_info));
  96. startup_info.cb = sizeof(startup_info);
  97. char p_name[255];
  98. std::strncpy(p_name, arg0, 255);
  99. const bool process_created = CreateProcessA(nullptr, // lpApplicationName
  100. p_name, // lpCommandLine
  101. nullptr, // lpProcessAttributes
  102. nullptr, // lpThreadAttributes
  103. false, // bInheritHandles
  104. 0, // dwCreationFlags
  105. nullptr, // lpEnvironment
  106. nullptr, // lpCurrentDirectory
  107. &startup_info, // lpStartupInfo
  108. pi // lpProcessInformation
  109. );
  110. if (!process_created) {
  111. std::fprintf(stderr, "CreateProcessA failed with error %d\n", GetLastError());
  112. return false;
  113. }
  114. return true;
  115. }
  116. #endif