summaryrefslogtreecommitdiffstats
path: root/src/yuzu
diff options
context:
space:
mode:
Diffstat (limited to 'src/yuzu')
-rw-r--r--src/yuzu/CMakeLists.txt10
-rw-r--r--src/yuzu/applets/qt_controller.cpp4
-rw-r--r--src/yuzu/configuration/config.cpp8
-rw-r--r--src/yuzu/configuration/config.h4
-rw-r--r--src/yuzu/configuration/configure_debug.cpp23
-rw-r--r--src/yuzu/configuration/configure_debug.h2
-rw-r--r--src/yuzu/configuration/configure_debug.ui132
-rw-r--r--src/yuzu/configuration/configure_input.cpp9
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp23
-rw-r--r--src/yuzu/configuration/configure_per_game.cpp3
-rw-r--r--src/yuzu/configuration/configure_tas.ui3
-rw-r--r--src/yuzu/configuration/configure_web.cpp21
-rw-r--r--src/yuzu/configuration/input_profiles.cpp11
-rw-r--r--src/yuzu/configuration/input_profiles.h4
-rw-r--r--src/yuzu/main.cpp62
-rw-r--r--src/yuzu/main.h2
-rw-r--r--src/yuzu/mini_dump.cpp202
-rw-r--r--src/yuzu/mini_dump.h19
-rw-r--r--src/yuzu/startup_checks.cpp116
-rw-r--r--src/yuzu/startup_checks.h7
20 files changed, 488 insertions, 177 deletions
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 50007338f..29d506c47 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -208,6 +208,16 @@ add_executable(yuzu
yuzu.rc
)
+if (WIN32 AND YUZU_CRASH_DUMPS)
+ target_sources(yuzu PRIVATE
+ mini_dump.cpp
+ mini_dump.h
+ )
+
+ target_link_libraries(yuzu PRIVATE ${DBGHELP_LIBRARY})
+ target_compile_definitions(yuzu PRIVATE -DYUZU_DBGHELP)
+endif()
+
file(GLOB COMPAT_LIST
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
diff --git a/src/yuzu/applets/qt_controller.cpp b/src/yuzu/applets/qt_controller.cpp
index 8be311fcb..12efdc216 100644
--- a/src/yuzu/applets/qt_controller.cpp
+++ b/src/yuzu/applets/qt_controller.cpp
@@ -63,7 +63,7 @@ QtControllerSelectorDialog::QtControllerSelectorDialog(
InputCommon::InputSubsystem* input_subsystem_, Core::System& system_)
: QDialog(parent), ui(std::make_unique<Ui::QtControllerSelectorDialog>()),
parameters(std::move(parameters_)), input_subsystem{input_subsystem_},
- input_profiles(std::make_unique<InputProfiles>(system_)), system{system_} {
+ input_profiles(std::make_unique<InputProfiles>()), system{system_} {
ui->setupUi(this);
player_widgets = {
@@ -291,7 +291,7 @@ bool QtControllerSelectorDialog::CheckIfParametersMet() {
// Here, we check and validate the current configuration against all applicable parameters.
const auto num_connected_players = static_cast<int>(
std::count_if(player_groupboxes.begin(), player_groupboxes.end(),
- [this](const QGroupBox* player) { return player->isChecked(); }));
+ [](const QGroupBox* player) { return player->isChecked(); }));
const auto min_supported_players = parameters.enable_single_mode ? 1 : parameters.min_players;
const auto max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players;
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 8ecd87150..195074bf2 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -15,8 +15,7 @@
namespace FS = Common::FS;
-Config::Config(Core::System& system_, const std::string& config_name, ConfigType config_type)
- : type(config_type), system{system_} {
+Config::Config(const std::string& config_name, ConfigType config_type) : type(config_type) {
global = config_type == ConfigType::GlobalConfig;
Initialize(config_name);
@@ -546,6 +545,8 @@ void Config::ReadDebuggingValues() {
ReadBasicSetting(Settings::values.use_debug_asserts);
ReadBasicSetting(Settings::values.use_auto_stub);
ReadBasicSetting(Settings::values.enable_all_controllers);
+ ReadBasicSetting(Settings::values.create_crash_dumps);
+ ReadBasicSetting(Settings::values.perform_vulkan_check);
qt_config->endGroup();
}
@@ -1161,6 +1162,8 @@ void Config::SaveDebuggingValues() {
WriteBasicSetting(Settings::values.use_debug_asserts);
WriteBasicSetting(Settings::values.disable_macro_jit);
WriteBasicSetting(Settings::values.enable_all_controllers);
+ WriteBasicSetting(Settings::values.create_crash_dumps);
+ WriteBasicSetting(Settings::values.perform_vulkan_check);
qt_config->endGroup();
}
@@ -1547,7 +1550,6 @@ void Config::Reload() {
ReadValues();
// To apply default value changes
SaveValues();
- system.ApplySettings();
}
void Config::Save() {
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index 486ceea94..06fa7d2d0 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -25,7 +25,7 @@ public:
InputProfile,
};
- explicit Config(Core::System& system_, const std::string& config_name = "qt-config",
+ explicit Config(const std::string& config_name = "qt-config",
ConfigType config_type = ConfigType::GlobalConfig);
~Config();
@@ -194,8 +194,6 @@ private:
std::unique_ptr<QSettings> qt_config;
std::string qt_config_loc;
bool global;
-
- Core::System& system;
};
// These metatype declarations cannot be in common/settings.h because core is devoid of QT
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index 04d397750..dacc75a20 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <QDesktopServices>
+#include <QMessageBox>
#include <QUrl>
#include "common/fs/path_util.h"
#include "common/logging/backend.h"
@@ -26,6 +27,16 @@ ConfigureDebug::ConfigureDebug(const Core::System& system_, QWidget* parent)
connect(ui->toggle_gdbstub, &QCheckBox::toggled,
[&]() { ui->gdbport_spinbox->setEnabled(ui->toggle_gdbstub->isChecked()); });
+
+ connect(ui->create_crash_dumps, &QCheckBox::stateChanged, [&](int) {
+ if (crash_dump_warning_shown) {
+ return;
+ }
+ QMessageBox::warning(this, tr("Restart Required"),
+ tr("yuzu is required to restart in order to apply this setting."),
+ QMessageBox::Ok, QMessageBox::Ok);
+ crash_dump_warning_shown = true;
+ });
}
ConfigureDebug::~ConfigureDebug() = default;
@@ -66,12 +77,20 @@ void ConfigureDebug::SetConfiguration() {
ui->disable_loop_safety_checks->setChecked(
Settings::values.disable_shader_loop_safety_checks.GetValue());
ui->extended_logging->setChecked(Settings::values.extended_logging.GetValue());
+ ui->perform_vulkan_check->setChecked(Settings::values.perform_vulkan_check.GetValue());
#ifdef YUZU_USE_QT_WEB_ENGINE
ui->disable_web_applet->setChecked(UISettings::values.disable_web_applet.GetValue());
#else
ui->disable_web_applet->setEnabled(false);
- ui->disable_web_applet->setText(QString::fromUtf8("Web applet not compiled"));
+ ui->disable_web_applet->setText(tr("Web applet not compiled"));
+#endif
+
+#ifdef YUZU_DBGHELP
+ ui->create_crash_dumps->setChecked(Settings::values.create_crash_dumps.GetValue());
+#else
+ ui->create_crash_dumps->setEnabled(false);
+ ui->create_crash_dumps->setText(tr("MiniDump creation not compiled"));
#endif
}
@@ -84,6 +103,7 @@ void ConfigureDebug::ApplyConfiguration() {
Settings::values.enable_fs_access_log = ui->fs_access_log->isChecked();
Settings::values.reporting_services = ui->reporting_services->isChecked();
Settings::values.dump_audio_commands = ui->dump_audio_commands->isChecked();
+ Settings::values.create_crash_dumps = ui->create_crash_dumps->isChecked();
Settings::values.quest_flag = ui->quest_flag->isChecked();
Settings::values.use_debug_asserts = ui->use_debug_asserts->isChecked();
Settings::values.use_auto_stub = ui->use_auto_stub->isChecked();
@@ -98,6 +118,7 @@ void ConfigureDebug::ApplyConfiguration() {
ui->disable_loop_safety_checks->isChecked();
Settings::values.disable_macro_jit = ui->disable_macro_jit->isChecked();
Settings::values.extended_logging = ui->extended_logging->isChecked();
+ Settings::values.perform_vulkan_check = ui->perform_vulkan_check->isChecked();
UISettings::values.disable_web_applet = ui->disable_web_applet->isChecked();
Debugger::ToggleConsole();
Common::Log::Filter filter;
diff --git a/src/yuzu/configuration/configure_debug.h b/src/yuzu/configuration/configure_debug.h
index 42d30f170..030a0b7f7 100644
--- a/src/yuzu/configuration/configure_debug.h
+++ b/src/yuzu/configuration/configure_debug.h
@@ -32,4 +32,6 @@ private:
std::unique_ptr<Ui::ConfigureDebug> ui;
const Core::System& system;
+
+ bool crash_dump_warning_shown{false};
};
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index 47b8b80f1..102c8c66c 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -7,60 +7,60 @@
</property>
<widget class="QWidget">
<layout class="QVBoxLayout" name="verticalLayout_1">
- <item>
- <layout class="QVBoxLayout" name="verticalLayout_2">
- <item>
- <widget class="QGroupBox" name="groupBox">
- <property name="title">
- <string>Debugger</string>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>Debugger</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_11">
<item>
- <layout class="QHBoxLayout" name="horizontalLayout_11">
- <item>
- <widget class="QCheckBox" name="toggle_gdbstub">
- <property name="text">
- <string>Enable GDB Stub</string>
- </property>
- </widget>
- </item>
- <item>
- <spacer name="horizontalSpacer">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
- <widget class="QLabel" name="label_11">
- <property name="text">
- <string>Port:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QSpinBox" name="gdbport_spinbox">
- <property name="minimum">
- <number>1024</number>
- </property>
- <property name="maximum">
- <number>65535</number>
- </property>
- </widget>
- </item>
- </layout>
+ <widget class="QCheckBox" name="toggle_gdbstub">
+ <property name="text">
+ <string>Enable GDB Stub</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_11">
+ <property name="text">
+ <string>Port:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QSpinBox" name="gdbport_spinbox">
+ <property name="minimum">
+ <number>1024</number>
+ </property>
+ <property name="maximum">
+ <number>65535</number>
+ </property>
+ </widget>
</item>
</layout>
- </widget>
- </item>
- </layout>
+ </item>
+ </layout>
+ </widget>
</item>
+ </layout>
+ </item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
@@ -231,6 +231,13 @@
<string>Debugging</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
+ <item row="2" column="0">
+ <widget class="QCheckBox" name="reporting_services">
+ <property name="text">
+ <string>Enable Verbose Reporting Services**</string>
+ </property>
+ </widget>
+ </item>
<item row="0" column="0">
<widget class="QCheckBox" name="fs_access_log">
<property name="text">
@@ -238,20 +245,20 @@
</property>
</widget>
</item>
- <item row="1" column="0">
+ <item row="0" column="1">
<widget class="QCheckBox" name="dump_audio_commands">
- <property name="text">
- <string>Dump Audio Commands To Console**</string>
- </property>
<property name="toolTip">
<string>Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer.</string>
</property>
+ <property name="text">
+ <string>Dump Audio Commands To Console**</string>
+ </property>
</widget>
</item>
- <item row="2" column="0">
- <widget class="QCheckBox" name="reporting_services">
+ <item row="2" column="1">
+ <widget class="QCheckBox" name="create_crash_dumps">
<property name="text">
- <string>Enable Verbose Reporting Services**</string>
+ <string>Create Minidump After Crash</string>
</property>
</widget>
</item>
@@ -306,6 +313,16 @@
</property>
</widget>
</item>
+ <item row="3" column="0">
+ <widget class="QCheckBox" name="perform_vulkan_check">
+ <property name="toolTip">
+ <string>Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu.</string>
+ </property>
+ <property name="text">
+ <string>Perform Startup Vulkan Check</string>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
</item>
@@ -340,7 +357,6 @@
<tabstop>disable_loop_safety_checks</tabstop>
<tabstop>fs_access_log</tabstop>
<tabstop>reporting_services</tabstop>
- <tabstop>dump_audio_commands</tabstop>
<tabstop>quest_flag</tabstop>
<tabstop>enable_cpu_debugging</tabstop>
<tabstop>use_debug_asserts</tabstop>
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index 16fba3deb..1db374d4a 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -65,7 +65,7 @@ void OnDockedModeChanged(bool last_state, bool new_state, Core::System& system)
ConfigureInput::ConfigureInput(Core::System& system_, QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()),
- profiles(std::make_unique<InputProfiles>(system_)), system{system_} {
+ profiles(std::make_unique<InputProfiles>()), system{system_} {
ui->setupUi(this);
}
@@ -163,10 +163,9 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
[this, input_subsystem, &hid_core] {
CallConfigureDialog<ConfigureRingController>(*this, input_subsystem, hid_core);
});
- connect(advanced, &ConfigureInputAdvanced::CallCameraDialog,
- [this, input_subsystem, &hid_core] {
- CallConfigureDialog<ConfigureCamera>(*this, input_subsystem);
- });
+ connect(advanced, &ConfigureInputAdvanced::CallCameraDialog, [this, input_subsystem] {
+ CallConfigureDialog<ConfigureCamera>(*this, input_subsystem);
+ });
connect(ui->vibrationButton, &QPushButton::clicked,
[this, &hid_core] { CallConfigureDialog<ConfigureVibration>(*this, hid_core); });
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 109689c88..9e5a40fe7 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -161,6 +161,7 @@ QString ConfigureInputPlayer::ButtonToText(const Common::ParamPackage& param) {
const QString toggle = QString::fromStdString(param.Get("toggle", false) ? "~" : "");
const QString inverted = QString::fromStdString(param.Get("inverted", false) ? "!" : "");
+ const QString invert = QString::fromStdString(param.Get("invert", "+") == "-" ? "-" : "");
const auto common_button_name = input_subsystem->GetButtonName(param);
// Retrieve the names from Qt
@@ -184,7 +185,7 @@ QString ConfigureInputPlayer::ButtonToText(const Common::ParamPackage& param) {
}
if (param.Has("axis")) {
const QString axis = QString::fromStdString(param.Get("axis", ""));
- return QObject::tr("%1%2Axis %3").arg(toggle, inverted, axis);
+ return QObject::tr("%1%2Axis %3").arg(toggle, invert, axis);
}
if (param.Has("axis_x") && param.Has("axis_y") && param.Has("axis_z")) {
const QString axis_x = QString::fromStdString(param.Get("axis_x", ""));
@@ -362,18 +363,18 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
button_map[button_id]->setText(tr("[not set]"));
});
if (param.Has("code") || param.Has("button") || param.Has("hat")) {
- context_menu.addAction(tr("Toggle button"), [&] {
- const bool toggle_value = !param.Get("toggle", false);
- param.Set("toggle", toggle_value);
- button_map[button_id]->setText(ButtonToText(param));
- emulated_controller->SetButtonParam(button_id, param);
- });
context_menu.addAction(tr("Invert button"), [&] {
const bool invert_value = !param.Get("inverted", false);
param.Set("inverted", invert_value);
button_map[button_id]->setText(ButtonToText(param));
emulated_controller->SetButtonParam(button_id, param);
});
+ context_menu.addAction(tr("Toggle button"), [&] {
+ const bool toggle_value = !param.Get("toggle", false);
+ param.Set("toggle", toggle_value);
+ button_map[button_id]->setText(ButtonToText(param));
+ emulated_controller->SetButtonParam(button_id, param);
+ });
}
if (param.Has("axis")) {
context_menu.addAction(tr("Invert axis"), [&] {
@@ -398,6 +399,12 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
}
emulated_controller->SetButtonParam(button_id, param);
});
+ context_menu.addAction(tr("Toggle axis"), [&] {
+ const bool toggle_value = !param.Get("toggle", false);
+ param.Set("toggle", toggle_value);
+ button_map[button_id]->setText(ButtonToText(param));
+ emulated_controller->SetButtonParam(button_id, param);
+ });
}
context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
});
@@ -1410,7 +1417,7 @@ void ConfigureInputPlayer::HandleClick(
ui->controllerFrame->BeginMappingAnalog(button_id);
}
- timeout_timer->start(2500); // Cancel after 2.5 seconds
+ timeout_timer->start(4000); // Cancel after 4 seconds
poll_timer->start(25); // Check for new inputs every 25ms
}
diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp
index af8343b2e..c3cb8f61d 100644
--- a/src/yuzu/configuration/configure_per_game.cpp
+++ b/src/yuzu/configuration/configure_per_game.cpp
@@ -42,8 +42,7 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st
const auto file_path = std::filesystem::path(Common::FS::ToU8String(file_name));
const auto config_file_name = title_id == 0 ? Common::FS::PathToUTF8String(file_path.filename())
: fmt::format("{:016X}", title_id);
- game_config =
- std::make_unique<Config>(system, config_file_name, Config::ConfigType::PerGameConfig);
+ game_config = std::make_unique<Config>(config_file_name, Config::ConfigType::PerGameConfig);
addons_tab = std::make_unique<ConfigurePerGameAddons>(system_, this);
audio_tab = std::make_unique<ConfigureAudio>(system_, this);
diff --git a/src/yuzu/configuration/configure_tas.ui b/src/yuzu/configuration/configure_tas.ui
index cf88a5bf0..625af0c89 100644
--- a/src/yuzu/configuration/configure_tas.ui
+++ b/src/yuzu/configuration/configure_tas.ui
@@ -16,6 +16,9 @@
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Reads controller input from scripts in the same format as TAS-nx scripts.&lt;br/&gt;For a more detailed explanation, please consult the &lt;a href=&quot;https://yuzu-emu.org/help/feature/tas/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;help page&lt;/span&gt;&lt;/a&gt; on the yuzu website.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
+ <property name="openExternalLinks">
+ <bool>true</bool>
+ </property>
</widget>
</item>
<item row="1" column="0" colspan="4">
diff --git a/src/yuzu/configuration/configure_web.cpp b/src/yuzu/configuration/configure_web.cpp
index d668c992b..ab526e4ca 100644
--- a/src/yuzu/configuration/configure_web.cpp
+++ b/src/yuzu/configuration/configure_web.cpp
@@ -128,20 +128,25 @@ void ConfigureWeb::RefreshTelemetryID() {
void ConfigureWeb::OnLoginChanged() {
if (ui->edit_token->text().isEmpty()) {
user_verified = true;
-
- const QPixmap pixmap = QIcon::fromTheme(QStringLiteral("checked")).pixmap(16);
- ui->label_token_verified->setPixmap(pixmap);
+ // Empty = no icon
+ ui->label_token_verified->setPixmap(QPixmap());
+ ui->label_token_verified->setToolTip(QString());
} else {
user_verified = false;
- const QPixmap pixmap = QIcon::fromTheme(QStringLiteral("failed")).pixmap(16);
+ // Show an info icon if it's been changed, clearer than showing failure
+ const QPixmap pixmap = QIcon::fromTheme(QStringLiteral("info")).pixmap(16);
ui->label_token_verified->setPixmap(pixmap);
+ ui->label_token_verified->setToolTip(
+ tr("Unverified, please click Verify before saving configuration", "Tooltip"));
}
}
void ConfigureWeb::VerifyLogin() {
ui->button_verify_login->setDisabled(true);
ui->button_verify_login->setText(tr("Verifying..."));
+ ui->label_token_verified->setPixmap(QIcon::fromTheme(QStringLiteral("sync")).pixmap(16));
+ ui->label_token_verified->setToolTip(tr("Verifying..."));
verify_watcher.setFuture(QtConcurrent::run(
[username = UsernameFromDisplayToken(ui->edit_token->text().toStdString()),
token = TokenFromDisplayToken(ui->edit_token->text().toStdString())] {
@@ -155,13 +160,13 @@ void ConfigureWeb::OnLoginVerified() {
if (verify_watcher.result()) {
user_verified = true;
- const QPixmap pixmap = QIcon::fromTheme(QStringLiteral("checked")).pixmap(16);
- ui->label_token_verified->setPixmap(pixmap);
+ ui->label_token_verified->setPixmap(QIcon::fromTheme(QStringLiteral("checked")).pixmap(16));
+ ui->label_token_verified->setToolTip(tr("Verified", "Tooltip"));
ui->username->setText(
QString::fromStdString(UsernameFromDisplayToken(ui->edit_token->text().toStdString())));
} else {
- const QPixmap pixmap = QIcon::fromTheme(QStringLiteral("failed")).pixmap(16);
- ui->label_token_verified->setPixmap(pixmap);
+ ui->label_token_verified->setPixmap(QIcon::fromTheme(QStringLiteral("failed")).pixmap(16));
+ ui->label_token_verified->setToolTip(tr("Verification failed", "Tooltip"));
ui->username->setText(tr("Unspecified"));
QMessageBox::critical(this, tr("Verification failed"),
tr("Verification failed. Check that you have entered your token "
diff --git a/src/yuzu/configuration/input_profiles.cpp b/src/yuzu/configuration/input_profiles.cpp
index 20b22e7de..9bb69cab1 100644
--- a/src/yuzu/configuration/input_profiles.cpp
+++ b/src/yuzu/configuration/input_profiles.cpp
@@ -27,7 +27,7 @@ std::filesystem::path GetNameWithoutExtension(std::filesystem::path filename) {
} // namespace
-InputProfiles::InputProfiles(Core::System& system_) : system{system_} {
+InputProfiles::InputProfiles() {
const auto input_profile_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "input";
if (!FS::IsDir(input_profile_loc)) {
@@ -43,8 +43,8 @@ InputProfiles::InputProfiles(Core::System& system_) : system{system_} {
if (IsINI(filename) && IsProfileNameValid(name_without_ext)) {
map_profiles.insert_or_assign(
- name_without_ext, std::make_unique<Config>(system, name_without_ext,
- Config::ConfigType::InputProfile));
+ name_without_ext,
+ std::make_unique<Config>(name_without_ext, Config::ConfigType::InputProfile));
}
return true;
@@ -67,6 +67,8 @@ std::vector<std::string> InputProfiles::GetInputProfileNames() {
profile_names.push_back(profile_name);
}
+ std::stable_sort(profile_names.begin(), profile_names.end());
+
return profile_names;
}
@@ -80,8 +82,7 @@ bool InputProfiles::CreateProfile(const std::string& profile_name, std::size_t p
}
map_profiles.insert_or_assign(
- profile_name,
- std::make_unique<Config>(system, profile_name, Config::ConfigType::InputProfile));
+ profile_name, std::make_unique<Config>(profile_name, Config::ConfigType::InputProfile));
return SaveProfile(profile_name, player_index);
}
diff --git a/src/yuzu/configuration/input_profiles.h b/src/yuzu/configuration/input_profiles.h
index 65fc9e62c..2bf3e4250 100644
--- a/src/yuzu/configuration/input_profiles.h
+++ b/src/yuzu/configuration/input_profiles.h
@@ -15,7 +15,7 @@ class Config;
class InputProfiles {
public:
- explicit InputProfiles(Core::System& system_);
+ explicit InputProfiles();
virtual ~InputProfiles();
std::vector<std::string> GetInputProfileNames();
@@ -31,6 +31,4 @@ private:
bool ProfileExistsInMap(const std::string& profile_name) const;
std::unordered_map<std::string, std::unique_ptr<Config>> map_profiles;
-
- Core::System& system;
};
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index deee1c370..612b8dbb8 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -138,6 +138,10 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
#include "yuzu/uisettings.h"
#include "yuzu/util/clickable_label.h"
+#ifdef YUZU_DBGHELP
+#include "yuzu/mini_dump.h"
+#endif
+
using namespace Common::Literals;
#ifdef USE_DISCORD_PRESENCE
@@ -269,10 +273,9 @@ bool GMainWindow::CheckDarkMode() {
#endif // __linux__
}
-GMainWindow::GMainWindow(bool has_broken_vulkan)
+GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan)
: ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()},
- input_subsystem{std::make_shared<InputCommon::InputSubsystem>()},
- config{std::make_unique<Config>(*system)},
+ input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, config{std::move(config_)},
vfs{std::make_shared<FileSys::RealVfsFilesystem>()},
provider{std::make_unique<FileSys::ManualContentProvider>()} {
#ifdef __linux__
@@ -1640,7 +1643,8 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
const auto config_file_name = title_id == 0
? Common::FS::PathToUTF8String(file_path.filename())
: fmt::format("{:016X}", title_id);
- Config per_game_config(*system, config_file_name, Config::ConfigType::PerGameConfig);
+ Config per_game_config(config_file_name, Config::ConfigType::PerGameConfig);
+ system->ApplySettings();
}
// Save configurations
@@ -1999,7 +2003,7 @@ static bool RomFSRawCopy(QProgressDialog& dialog, const FileSys::VirtualDir& src
}
void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type) {
- const QString entry_type = [this, type] {
+ const QString entry_type = [type] {
switch (type) {
case InstalledEntryType::Game:
return tr("Contents");
@@ -2096,7 +2100,7 @@ void GMainWindow::RemoveAddOnContent(u64 program_id, const QString& entry_type)
void GMainWindow::OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target,
const std::string& game_path) {
- const QString question = [this, target] {
+ const QString question = [target] {
switch (target) {
case GameListRemoveTarget::GlShaderCache:
return tr("Delete OpenGL Transferable Shader Cache?");
@@ -2989,7 +2993,7 @@ void GMainWindow::OnConfigure() {
Settings::values.disabled_addons.clear();
- config = std::make_unique<Config>(*system);
+ config = std::make_unique<Config>();
UISettings::values.reset_to_defaults = false;
UISettings::values.game_dirs = std::move(old_game_dirs);
@@ -3050,6 +3054,7 @@ void GMainWindow::OnConfigure() {
UpdateStatusButtons();
controller_dialog->refreshConfiguration();
+ system->ApplySettings();
}
void GMainWindow::OnConfigureTas() {
@@ -3262,26 +3267,7 @@ void GMainWindow::LoadAmiibo(const QString& filename) {
return;
}
- QFile nfc_file{filename};
- if (!nfc_file.open(QIODevice::ReadOnly)) {
- QMessageBox::warning(this, tr("Error opening Amiibo data file"),
- tr("Unable to open Amiibo file \"%1\" for reading.").arg(filename));
- return;
- }
-
- const u64 nfc_file_size = nfc_file.size();
- std::vector<u8> buffer(nfc_file_size);
- const u64 read_size = nfc_file.read(reinterpret_cast<char*>(buffer.data()), nfc_file_size);
- if (nfc_file_size != read_size) {
- QMessageBox::warning(this, tr("Error reading Amiibo data file"),
- tr("Unable to fully read Amiibo data. Expected to read %1 bytes, but "
- "was only able to read %2 bytes.")
- .arg(nfc_file_size)
- .arg(read_size));
- return;
- }
-
- if (!nfc->LoadAmiibo(buffer)) {
+ if (!nfc->LoadAmiibo(filename.toStdString())) {
QMessageBox::warning(this, tr("Error loading Amiibo data"),
tr("Unable to load Amiibo data."));
}
@@ -4090,8 +4076,26 @@ void GMainWindow::changeEvent(QEvent* event) {
#endif
int main(int argc, char* argv[]) {
+ std::unique_ptr<Config> config = std::make_unique<Config>();
bool has_broken_vulkan = false;
- if (StartupChecks(argv[0], &has_broken_vulkan)) {
+ bool is_child = false;
+ if (CheckEnvVars(&is_child)) {
+ return 0;
+ }
+
+#ifdef YUZU_DBGHELP
+ PROCESS_INFORMATION pi;
+ if (!is_child && Settings::values.create_crash_dumps.GetValue() &&
+ MiniDump::SpawnDebuggee(argv[0], pi)) {
+ // Delete the config object so that it doesn't save when the program exits
+ config.reset(nullptr);
+ MiniDump::DebugDebuggee(pi);
+ return 0;
+ }
+#endif
+
+ if (StartupChecks(argv[0], &has_broken_vulkan,
+ Settings::values.perform_vulkan_check.GetValue())) {
return 0;
}
@@ -4143,7 +4147,7 @@ int main(int argc, char* argv[]) {
// generating shaders
setlocale(LC_ALL, "C");
- GMainWindow main_window{has_broken_vulkan};
+ GMainWindow main_window{std::move(config), has_broken_vulkan};
// After settings have been loaded by GMainWindow, apply the filter
main_window.show();
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 1a756b171..f7aa8e417 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -120,7 +120,7 @@ class GMainWindow : public QMainWindow {
public:
void filterBarSetChecked(bool state);
void UpdateUITheme();
- explicit GMainWindow(bool has_broken_vulkan);
+ explicit GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan);
~GMainWindow() override;
bool DropAction(QDropEvent* event);
diff --git a/src/yuzu/mini_dump.cpp b/src/yuzu/mini_dump.cpp
new file mode 100644
index 000000000..a34dc6a9c
--- /dev/null
+++ b/src/yuzu/mini_dump.cpp
@@ -0,0 +1,202 @@
+// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <cstdio>
+#include <cstring>
+#include <ctime>
+#include <filesystem>
+#include <fmt/format.h>
+#include <windows.h>
+#include "yuzu/mini_dump.h"
+#include "yuzu/startup_checks.h"
+
+// dbghelp.h must be included after windows.h
+#include <dbghelp.h>
+
+namespace MiniDump {
+
+void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_INFORMATION* info,
+ EXCEPTION_POINTERS* pep) {
+ char file_name[255];
+ const std::time_t the_time = std::time(nullptr);
+ std::strftime(file_name, 255, "yuzu-crash-%Y%m%d%H%M%S.dmp", std::localtime(&the_time));
+
+ // Open the file
+ HANDLE file_handle = CreateFileA(file_name, GENERIC_READ | GENERIC_WRITE, 0, nullptr,
+ CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
+
+ if (file_handle == nullptr || file_handle == INVALID_HANDLE_VALUE) {
+ fmt::print(stderr, "CreateFileA failed. Error: {}", GetLastError());
+ return;
+ }
+
+ // Create the minidump
+ const MINIDUMP_TYPE dump_type = MiniDumpNormal;
+
+ const bool write_dump_status = MiniDumpWriteDump(process_handle, process_id, file_handle,
+ dump_type, (pep != 0) ? info : 0, 0, 0);
+
+ if (write_dump_status) {
+ fmt::print(stderr, "MiniDump created: {}", file_name);
+ } else {
+ fmt::print(stderr, "MiniDumpWriteDump failed. Error: {}", GetLastError());
+ }
+
+ // Close the file
+ CloseHandle(file_handle);
+}
+
+void DumpFromDebugEvent(DEBUG_EVENT& deb_ev, PROCESS_INFORMATION& pi) {
+ EXCEPTION_RECORD& record = deb_ev.u.Exception.ExceptionRecord;
+
+ HANDLE thread_handle = OpenThread(THREAD_GET_CONTEXT, false, deb_ev.dwThreadId);
+ if (thread_handle == nullptr) {
+ fmt::print(stderr, "OpenThread failed ({})", GetLastError());
+ return;
+ }
+
+ // Get child process context
+ CONTEXT context = {};
+ context.ContextFlags = CONTEXT_ALL;
+ if (!GetThreadContext(thread_handle, &context)) {
+ fmt::print(stderr, "GetThreadContext failed ({})", GetLastError());
+ return;
+ }
+
+ // Create exception pointers for minidump
+ EXCEPTION_POINTERS ep;
+ ep.ExceptionRecord = &record;
+ ep.ContextRecord = &context;
+
+ MINIDUMP_EXCEPTION_INFORMATION info;
+ info.ThreadId = deb_ev.dwThreadId;
+ info.ExceptionPointers = &ep;
+ info.ClientPointers = false;
+
+ CreateMiniDump(pi.hProcess, pi.dwProcessId, &info, &ep);
+
+ if (CloseHandle(thread_handle) == 0) {
+ fmt::print(stderr, "error: CloseHandle(thread_handle) failed ({})", GetLastError());
+ }
+}
+
+bool SpawnDebuggee(const char* arg0, PROCESS_INFORMATION& pi) {
+ std::memset(&pi, 0, sizeof(pi));
+
+ // Don't debug if we are already being debugged
+ if (IsDebuggerPresent()) {
+ return false;
+ }
+
+ if (!SpawnChild(arg0, &pi, 0)) {
+ fmt::print(stderr, "warning: continuing without crash dumps");
+ return false;
+ }
+
+ const bool can_debug = DebugActiveProcess(pi.dwProcessId);
+ if (!can_debug) {
+ fmt::print(stderr,
+ "warning: DebugActiveProcess failed ({}), continuing without crash dumps",
+ GetLastError());
+ return false;
+ }
+
+ return true;
+}
+
+static const char* ExceptionName(DWORD exception) {
+ switch (exception) {
+ case EXCEPTION_ACCESS_VIOLATION:
+ return "EXCEPTION_ACCESS_VIOLATION";
+ case EXCEPTION_DATATYPE_MISALIGNMENT:
+ return "EXCEPTION_DATATYPE_MISALIGNMENT";
+ case EXCEPTION_BREAKPOINT:
+ return "EXCEPTION_BREAKPOINT";
+ case EXCEPTION_SINGLE_STEP:
+ return "EXCEPTION_SINGLE_STEP";
+ case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
+ return "EXCEPTION_ARRAY_BOUNDS_EXCEEDED";
+ case EXCEPTION_FLT_DENORMAL_OPERAND:
+ return "EXCEPTION_FLT_DENORMAL_OPERAND";
+ case EXCEPTION_FLT_DIVIDE_BY_ZERO:
+ return "EXCEPTION_FLT_DIVIDE_BY_ZERO";
+ case EXCEPTION_FLT_INEXACT_RESULT:
+ return "EXCEPTION_FLT_INEXACT_RESULT";
+ case EXCEPTION_FLT_INVALID_OPERATION:
+ return "EXCEPTION_FLT_INVALID_OPERATION";
+ case EXCEPTION_FLT_OVERFLOW:
+ return "EXCEPTION_FLT_OVERFLOW";
+ case EXCEPTION_FLT_STACK_CHECK:
+ return "EXCEPTION_FLT_STACK_CHECK";
+ case EXCEPTION_FLT_UNDERFLOW:
+ return "EXCEPTION_FLT_UNDERFLOW";
+ case EXCEPTION_INT_DIVIDE_BY_ZERO:
+ return "EXCEPTION_INT_DIVIDE_BY_ZERO";
+ case EXCEPTION_INT_OVERFLOW:
+ return "EXCEPTION_INT_OVERFLOW";
+ case EXCEPTION_PRIV_INSTRUCTION:
+ return "EXCEPTION_PRIV_INSTRUCTION";
+ case EXCEPTION_IN_PAGE_ERROR:
+ return "EXCEPTION_IN_PAGE_ERROR";
+ case EXCEPTION_ILLEGAL_INSTRUCTION:
+ return "EXCEPTION_ILLEGAL_INSTRUCTION";
+ case EXCEPTION_NONCONTINUABLE_EXCEPTION:
+ return "EXCEPTION_NONCONTINUABLE_EXCEPTION";
+ case EXCEPTION_STACK_OVERFLOW:
+ return "EXCEPTION_STACK_OVERFLOW";
+ case EXCEPTION_INVALID_DISPOSITION:
+ return "EXCEPTION_INVALID_DISPOSITION";
+ case EXCEPTION_GUARD_PAGE:
+ return "EXCEPTION_GUARD_PAGE";
+ case EXCEPTION_INVALID_HANDLE:
+ return "EXCEPTION_INVALID_HANDLE";
+ default:
+ return "unknown exception type";
+ }
+}
+
+void DebugDebuggee(PROCESS_INFORMATION& pi) {
+ DEBUG_EVENT deb_ev = {};
+
+ while (deb_ev.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT) {
+ const bool wait_success = WaitForDebugEvent(&deb_ev, INFINITE);
+ if (!wait_success) {
+ fmt::print(stderr, "error: WaitForDebugEvent failed ({})", GetLastError());
+ return;
+ }
+
+ switch (deb_ev.dwDebugEventCode) {
+ case OUTPUT_DEBUG_STRING_EVENT:
+ case CREATE_PROCESS_DEBUG_EVENT:
+ case CREATE_THREAD_DEBUG_EVENT:
+ case EXIT_PROCESS_DEBUG_EVENT:
+ case EXIT_THREAD_DEBUG_EVENT:
+ case LOAD_DLL_DEBUG_EVENT:
+ case RIP_EVENT:
+ case UNLOAD_DLL_DEBUG_EVENT:
+ // Continue on all other debug events
+ ContinueDebugEvent(deb_ev.dwProcessId, deb_ev.dwThreadId, DBG_CONTINUE);
+ break;
+ case EXCEPTION_DEBUG_EVENT:
+ EXCEPTION_RECORD& record = deb_ev.u.Exception.ExceptionRecord;
+
+ // We want to generate a crash dump if we are seeing the same exception again.
+ if (!deb_ev.u.Exception.dwFirstChance) {
+ fmt::print(stderr, "Creating MiniDump on ExceptionCode: 0x{:08x} {}\n",
+ record.ExceptionCode, ExceptionName(record.ExceptionCode));
+ DumpFromDebugEvent(deb_ev, pi);
+ }
+
+ // Continue without handling the exception.
+ // Lets the debuggee use its own exception handler.
+ // - If one does not exist, we will see the exception once more where we make a minidump
+ // for. Then when it reaches here again, yuzu will probably crash.
+ // - DBG_CONTINUE on an exception that the debuggee does not handle can set us up for an
+ // infinite loop of exceptions.
+ ContinueDebugEvent(deb_ev.dwProcessId, deb_ev.dwThreadId, DBG_EXCEPTION_NOT_HANDLED);
+ break;
+ }
+ }
+}
+
+} // namespace MiniDump
diff --git a/src/yuzu/mini_dump.h b/src/yuzu/mini_dump.h
new file mode 100644
index 000000000..d6b6cca84
--- /dev/null
+++ b/src/yuzu/mini_dump.h
@@ -0,0 +1,19 @@
+// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <windows.h>
+
+#include <dbghelp.h>
+
+namespace MiniDump {
+
+void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_INFORMATION* info,
+ EXCEPTION_POINTERS* pep);
+
+void DumpFromDebugEvent(DEBUG_EVENT& deb_ev, PROCESS_INFORMATION& pi);
+bool SpawnDebuggee(const char* arg0, PROCESS_INFORMATION& pi);
+void DebugDebuggee(PROCESS_INFORMATION& pi);
+
+} // namespace MiniDump
diff --git a/src/yuzu/startup_checks.cpp b/src/yuzu/startup_checks.cpp
index 8421280bf..fc2693f9d 100644
--- a/src/yuzu/startup_checks.cpp
+++ b/src/yuzu/startup_checks.cpp
@@ -31,48 +31,68 @@ void CheckVulkan() {
}
}
-bool StartupChecks(const char* arg0, bool* has_broken_vulkan) {
+bool CheckEnvVars(bool* is_child) {
#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) {
+ if (startup_check_var > 0 && std::strncmp(variable_contents, ENV_VAR_ENABLED_TEXT, 8) == 0) {
CheckVulkan();
return true;
}
+ // Don't perform startup checks if we are a child process
+ 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)) {
+ std::fprintf(stderr, "SetEnvironmentVariableA failed to set %s with error %d\n",
+ IS_CHILD_ENV_VAR, GetLastError());
+ return true;
+ }
+#endif
+ return false;
+}
+
+bool StartupChecks(const char* arg0, bool* has_broken_vulkan, bool perform_vulkan_check) {
+#ifdef _WIN32
// Set the startup variable for child processes
- const bool env_var_set = SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, "ON");
+ const bool env_var_set = SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, ENV_VAR_ENABLED_TEXT);
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 (perform_vulkan_check) {
+ // Spawn child process that performs Vulkan check
+ PROCESS_INFORMATION process_info;
+ std::memset(&process_info, '\0', sizeof(process_info));
+
+ if (!SpawnChild(arg0, &process_info, 0)) {
+ 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)) {
@@ -81,32 +101,34 @@ bool StartupChecks(const char* arg0, bool* has_broken_vulkan) {
}
#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;
+ if (perform_vulkan_check) {
+ 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);
}
- // 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) {
+bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi, int flags) {
STARTUPINFOA startup_info;
std::memset(&startup_info, '\0', sizeof(startup_info));
@@ -120,7 +142,7 @@ bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi) {
nullptr, // lpProcessAttributes
nullptr, // lpThreadAttributes
false, // bInheritHandles
- 0, // dwCreationFlags
+ flags, // dwCreationFlags
nullptr, // lpEnvironment
nullptr, // lpCurrentDirectory
&startup_info, // lpStartupInfo
diff --git a/src/yuzu/startup_checks.h b/src/yuzu/startup_checks.h
index 096dd54a8..d8e563be6 100644
--- a/src/yuzu/startup_checks.h
+++ b/src/yuzu/startup_checks.h
@@ -7,11 +7,14 @@
#include <windows.h>
#endif
+constexpr char IS_CHILD_ENV_VAR[] = "YUZU_IS_CHILD";
constexpr char STARTUP_CHECK_ENV_VAR[] = "YUZU_DO_STARTUP_CHECKS";
+constexpr char ENV_VAR_ENABLED_TEXT[] = "ON";
void CheckVulkan();
-bool StartupChecks(const char* arg0, bool* has_broken_vulkan);
+bool CheckEnvVars(bool* is_child);
+bool StartupChecks(const char* arg0, bool* has_broken_vulkan, bool perform_vulkan_check);
#ifdef _WIN32
-bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi);
+bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi, int flags);
#endif