1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "video_core/vulkan_common/vulkan_wrapper.h"
#ifdef _WIN32
#include <cstring> // for memset, strncpy
#include <processthreadsapi.h>
#include <windows.h>
#elif defined(YUZU_UNIX)
#include <errno.h>
#include <sys/wait.h>
#include <unistd.h>
#endif
#include <cstdio>
#include "video_core/vulkan_common/vulkan_instance.h"
#include "video_core/vulkan_common/vulkan_library.h"
#include "yuzu/startup_checks.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();
const Vulkan::vk::Instance instance =
Vulkan::CreateInstance(library, dld, VK_API_VERSION_1_0);
} catch (const Vulkan::vk::Exception& exception) {
std::fprintf(stderr, "Failed to initialize Vulkan: %s\n", exception.what());
}
}
bool StartupChecks(const char* arg0, bool* has_broken_vulkan) {
#ifdef _WIN32
// Check environment variable to see if we are the child
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, "ON", 8) == 0) {
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 with error %d\n",
STARTUP_CHECK_ENV_VAR, GetLastError());
return false;
}
PROCESS_INFORMATION process_info;
std::memset(&process_info, '\0', sizeof(process_info));
if (!SpawnChild(arg0, &process_info)) {
return false;
}
// Wait until the processs exits and get exit code from it
WaitForSingleObject(process_info.hProcess, INFINITE);
DWORD 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());
}
// Vulkan is broken if the child crashed (return value is not zero)
*has_broken_vulkan = (exit_code != 0);
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());
}
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) {
CheckVulkan();
return true;
} else if (pid == -1) {
const int err = errno;
std::fprintf(stderr, "fork failed with error %d\n", err);
return false;
}
// Get exit code from child process
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;
}
// Vulkan is broken if the child crashed (return value is not zero)
*has_broken_vulkan = (status != 0);
#endif
return false;
}
#ifdef _WIN32
bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi) {
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, 255);
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
pi // lpProcessInformation
);
if (!process_created) {
std::fprintf(stderr, "CreateProcessA failed with error %d\n", GetLastError());
return false;
}
return true;
}
#endif
|