From 6b51afaf1f8611d95379bcd4445fed2bef96a699 Mon Sep 17 00:00:00 2001 From: GPUCode Date: Tue, 25 Jul 2023 14:04:08 +0300 Subject: [PATCH] citra_qt: Add vulkan options to the GUI --- src/citra_qt/CMakeLists.txt | 2 + .../configuration/configure_debug.cpp | 35 ++++++++ src/citra_qt/configuration/configure_debug.ui | 29 ++++--- .../configuration/configure_dialog.cpp | 4 +- src/citra_qt/configuration/configure_dialog.h | 3 + .../configuration/configure_graphics.cpp | 79 ++++++++++++++++++- .../configuration/configure_graphics.h | 9 ++- .../configuration/configure_graphics.ui | 59 +++++++++++++- .../configuration/configure_per_game.cpp | 4 +- .../configuration/configure_per_game.h | 5 +- src/citra_qt/main.cpp | 13 ++- src/citra_qt/main.h | 4 + src/citra_qt/util/vk_device_info.cpp | 23 ++++++ src/citra_qt/util/vk_device_info.h | 11 +++ 14 files changed, 256 insertions(+), 24 deletions(-) create mode 100644 src/citra_qt/util/vk_device_info.cpp create mode 100644 src/citra_qt/util/vk_device_info.h diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index e04b71783..b33729ffb 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt @@ -185,6 +185,8 @@ add_executable(citra-qt util/spinbox.h util/util.cpp util/util.h + util/vk_device_info.cpp + util/vk_device_info.h ) file(GLOB COMPAT_LIST diff --git a/src/citra_qt/configuration/configure_debug.cpp b/src/citra_qt/configuration/configure_debug.cpp index 3e166834b..98d95c9e0 100644 --- a/src/citra_qt/configuration/configure_debug.cpp +++ b/src/citra_qt/configuration/configure_debug.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include +#include #include #include "citra_qt/configuration/configuration_shared.h" #include "citra_qt/configuration/configure_debug.h" @@ -12,6 +13,7 @@ #include "common/logging/backend.h" #include "common/settings.h" #include "ui_configure_debug.h" +#include "video_core/renderer_vulkan/vk_instance.h" // The QSlider doesn't have an easy way to set a custom step amount, // so we can just convert from the sliders range (0 - 79) to the expected @@ -34,8 +36,39 @@ ConfigureDebug::ConfigureDebug(bool is_powered_on_, QWidget* parent) QDesktopServices::openUrl(QUrl::fromLocalFile(path)); }); + connect(ui->toggle_renderer_debug, &QCheckBox::clicked, this, [this](bool checked) { + if (checked && Settings::values.graphics_api.GetValue() == Settings::GraphicsAPI::Vulkan) { + try { + Vulkan::Instance debug_inst{true}; + } catch (vk::LayerNotPresentError&) { + ui->toggle_renderer_debug->toggle(); + QMessageBox::warning(this, tr("Validation layer not available"), + tr("Unable to enable debug renderer because the layer " + "VK_LAYER_KHRONOS_validation is missing. " + "Please install the Vulkan SDK or the appropriate package " + "of your distribution")); + } + } + }); + + connect(ui->toggle_dump_command_buffers, &QCheckBox::clicked, this, [this](bool checked) { + if (checked && Settings::values.graphics_api.GetValue() == Settings::GraphicsAPI::Vulkan) { + try { + Vulkan::Instance debug_inst{false, true}; + } catch (vk::LayerNotPresentError&) { + ui->toggle_dump_command_buffers->toggle(); + QMessageBox::warning(this, tr("Command buffer dumping not available"), + tr("Unable to enable command buffer dumping because the layer " + "VK_LAYER_LUNARG_api_dump is missing. " + "Please install the Vulkan SDK or the appropriate package " + "of your distribution")); + } + } + }); + ui->toggle_cpu_jit->setEnabled(!is_powered_on); ui->toggle_renderer_debug->setEnabled(!is_powered_on); + ui->toggle_dump_command_buffers->setEnabled(!is_powered_on); // Set a minimum width for the label to prevent the slider from changing size. // This scales across DPIs. (This value should be enough for "xxx%") @@ -62,6 +95,7 @@ void ConfigureDebug::SetConfiguration() { ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter.GetValue())); ui->toggle_cpu_jit->setChecked(Settings::values.use_cpu_jit.GetValue()); ui->toggle_renderer_debug->setChecked(Settings::values.renderer_debug.GetValue()); + ui->toggle_dump_command_buffers->setChecked(Settings::values.dump_command_buffers.GetValue()); if (!Settings::IsConfiguringGlobal()) { if (Settings::values.cpu_clock_percentage.UsingGlobal()) { @@ -92,6 +126,7 @@ void ConfigureDebug::ApplyConfiguration() { Common::Log::SetGlobalFilter(filter); Settings::values.use_cpu_jit = ui->toggle_cpu_jit->isChecked(); Settings::values.renderer_debug = ui->toggle_renderer_debug->isChecked(); + Settings::values.dump_command_buffers = ui->toggle_dump_command_buffers->isChecked(); ConfigurationShared::ApplyPerGameSetting( &Settings::values.cpu_clock_percentage, ui->clock_speed_combo, diff --git a/src/citra_qt/configuration/configure_debug.ui b/src/citra_qt/configuration/configure_debug.ui index afdbc2b8f..20b84441c 100644 --- a/src/citra_qt/configuration/configure_debug.ui +++ b/src/citra_qt/configuration/configure_debug.ui @@ -7,7 +7,7 @@ 0 0 523 - 447 + 458 @@ -112,16 +112,6 @@ CPU - - - - <html><head/><body><p>Enables the use of the ARM JIT compiler for emulating the 3DS CPUs. Don't disable unless for debugging purposes</p></body></html> - - - Enable CPU JIT - - - @@ -202,6 +192,16 @@ + + + + <html><head/><body><p>Enables the use of the ARM JIT compiler for emulating the 3DS CPUs. Don't disable unless for debugging purposes</p></body></html> + + + Enable CPU JIT + + + @@ -209,6 +209,13 @@ + + + + Dump command buffers + + + diff --git a/src/citra_qt/configuration/configure_dialog.cpp b/src/citra_qt/configuration/configure_dialog.cpp index ee46ff5fb..1e19f5b3c 100644 --- a/src/citra_qt/configuration/configure_dialog.cpp +++ b/src/citra_qt/configuration/configure_dialog.cpp @@ -23,14 +23,14 @@ #include "ui_configure.h" ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, Core::System& system_, - bool enable_web_config) + std::span physical_devices, bool enable_web_config) : QDialog(parent), ui{std::make_unique()}, registry{registry_}, system{system_}, is_powered_on{system.IsPoweredOn()}, general_tab{std::make_unique(this)}, system_tab{std::make_unique(system, this)}, input_tab{std::make_unique(this)}, hotkeys_tab{std::make_unique(this)}, - graphics_tab{std::make_unique(is_powered_on, this)}, + graphics_tab{std::make_unique(physical_devices, is_powered_on, this)}, enhancements_tab{std::make_unique(this)}, audio_tab{std::make_unique(is_powered_on, this)}, camera_tab{std::make_unique(this)}, diff --git a/src/citra_qt/configuration/configure_dialog.h b/src/citra_qt/configuration/configure_dialog.h index 7a4ae96d2..8f8170fe0 100644 --- a/src/citra_qt/configuration/configure_dialog.h +++ b/src/citra_qt/configuration/configure_dialog.h @@ -5,7 +5,9 @@ #pragma once #include +#include #include +#include class HotkeyRegistry; @@ -35,6 +37,7 @@ class ConfigureDialog : public QDialog { public: explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry, Core::System& system, + std::span physical_devices, bool enable_web_config = true); ~ConfigureDialog() override; diff --git a/src/citra_qt/configuration/configure_graphics.cpp b/src/citra_qt/configuration/configure_graphics.cpp index 3951728e9..34a433586 100644 --- a/src/citra_qt/configuration/configure_graphics.cpp +++ b/src/citra_qt/configuration/configure_graphics.cpp @@ -8,15 +8,32 @@ #include "common/settings.h" #include "ui_configure_graphics.h" -ConfigureGraphics::ConfigureGraphics(bool is_powered_on, QWidget* parent) +ConfigureGraphics::ConfigureGraphics(std::span physical_devices, bool is_powered_on, + QWidget* parent) : QWidget(parent), ui(std::make_unique()) { ui->setupUi(this); + SetupPerGameUI(); + + for (const QString& name : physical_devices) { + ui->physical_device_combo->addItem(name); + } + ui->toggle_vsync_new->setEnabled(!is_powered_on); ui->graphics_api_combo->setEnabled(!is_powered_on); + ui->physical_device_combo->setEnabled(!is_powered_on); + ui->toggle_async_shaders->setEnabled(!is_powered_on); + ui->toggle_async_present->setEnabled(!is_powered_on); // Set the index to -1 to ensure the below lambda is called with setCurrentIndex ui->graphics_api_combo->setCurrentIndex(-1); + if (physical_devices.empty()) { + const u32 index = static_cast(Settings::GraphicsAPI::Vulkan); + ui->graphics_api_combo->removeItem(index); + ui->physical_device_combo->setVisible(false); + ui->spirv_shader_gen->setVisible(false); + } + connect(ui->graphics_api_combo, qOverload(&QComboBox::currentIndexChanged), this, [this](int index) { const auto graphics_api = @@ -35,7 +52,9 @@ ConfigureGraphics::ConfigureGraphics(bool is_powered_on, QWidget* parent) ui->toggle_disk_shader_cache->setEnabled(checked && enabled); }); - SetupPerGameUI(); + connect(ui->graphics_api_combo, qOverload(&QComboBox::currentIndexChanged), this, + &ConfigureGraphics::SetPhysicalDeviceComboVisibility); + SetConfiguration(); } @@ -47,15 +66,24 @@ void ConfigureGraphics::SetConfiguration() { !Settings::values.graphics_api.UsingGlobal()); ConfigurationShared::SetPerGameSetting(ui->graphics_api_combo, &Settings::values.graphics_api); + ConfigurationShared::SetHighlight(ui->physical_device_group, + !Settings::values.physical_device.UsingGlobal()); + ConfigurationShared::SetPerGameSetting(ui->physical_device_combo, + &Settings::values.physical_device); } else { ui->graphics_api_combo->setCurrentIndex( static_cast(Settings::values.graphics_api.GetValue())); + ui->physical_device_combo->setCurrentIndex( + static_cast(Settings::values.physical_device.GetValue())); } ui->toggle_hw_shader->setChecked(Settings::values.use_hw_shader.GetValue()); ui->toggle_accurate_mul->setChecked(Settings::values.shaders_accurate_mul.GetValue()); ui->toggle_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache.GetValue()); ui->toggle_vsync_new->setChecked(Settings::values.use_vsync_new.GetValue()); + ui->spirv_shader_gen->setChecked(Settings::values.spirv_shader_gen.GetValue()); + ui->toggle_async_shaders->setChecked(Settings::values.async_shader_compilation.GetValue()); + ui->toggle_async_present->setChecked(Settings::values.async_presentation.GetValue()); if (Settings::IsConfiguringGlobal()) { ui->toggle_shader_jit->setChecked(Settings::values.use_shader_jit.GetValue()); @@ -65,6 +93,14 @@ void ConfigureGraphics::SetConfiguration() { void ConfigureGraphics::ApplyConfiguration() { ConfigurationShared::ApplyPerGameSetting(&Settings::values.graphics_api, ui->graphics_api_combo); + ConfigurationShared::ApplyPerGameSetting(&Settings::values.physical_device, + ui->physical_device_combo); + ConfigurationShared::ApplyPerGameSetting(&Settings::values.async_shader_compilation, + ui->toggle_async_shaders, async_shader_compilation); + ConfigurationShared::ApplyPerGameSetting(&Settings::values.async_presentation, + ui->toggle_async_present, async_presentation); + ConfigurationShared::ApplyPerGameSetting(&Settings::values.spirv_shader_gen, + ui->spirv_shader_gen, spirv_shader_gen); ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_hw_shader, ui->toggle_hw_shader, use_hw_shader); ConfigurationShared::ApplyPerGameSetting(&Settings::values.shaders_accurate_mul, @@ -93,6 +129,11 @@ void ConfigureGraphics::SetupPerGameUI() { Settings::values.use_disk_shader_cache.UsingGlobal()); ui->toggle_vsync_new->setEnabled(ui->toggle_vsync_new->isEnabled() && Settings::values.use_vsync_new.UsingGlobal()); + ui->toggle_async_shaders->setEnabled( + Settings::values.async_shader_compilation.UsingGlobal()); + ui->toggle_async_present->setEnabled(Settings::values.async_presentation.UsingGlobal()); + ui->graphics_api_combo->setEnabled(Settings::values.graphics_api.UsingGlobal()); + ui->physical_device_combo->setEnabled(Settings::values.physical_device.UsingGlobal()); return; } @@ -102,6 +143,10 @@ void ConfigureGraphics::SetupPerGameUI() { ui->graphics_api_combo, ui->graphics_api_group, static_cast(Settings::values.graphics_api.GetValue(true))); + ConfigurationShared::SetColoredComboBox( + ui->physical_device_combo, ui->physical_device_group, + static_cast(Settings::values.physical_device.GetValue(true))); + ConfigurationShared::SetColoredTristate(ui->toggle_hw_shader, Settings::values.use_hw_shader, use_hw_shader); ConfigurationShared::SetColoredTristate( @@ -111,4 +156,34 @@ void ConfigureGraphics::SetupPerGameUI() { use_disk_shader_cache); ConfigurationShared::SetColoredTristate(ui->toggle_vsync_new, Settings::values.use_vsync_new, use_vsync_new); + ConfigurationShared::SetColoredTristate(ui->toggle_async_shaders, + Settings::values.async_shader_compilation, + async_shader_compilation); + ConfigurationShared::SetColoredTristate( + ui->toggle_async_present, Settings::values.async_presentation, async_presentation); + ConfigurationShared::SetColoredTristate(ui->spirv_shader_gen, Settings::values.spirv_shader_gen, + spirv_shader_gen); +} + +void ConfigureGraphics::SetPhysicalDeviceComboVisibility(int index) { + bool is_visible{}; + + // When configuring per-game the physical device combo should be + // shown either when the global api is used and that is Vulkan or + // Vulkan is set as the per-game api. + if (!Settings::IsConfiguringGlobal()) { + const auto global_graphics_api = Settings::values.graphics_api.GetValue(true); + const bool using_global = index == 0; + if (!using_global) { + index -= ConfigurationShared::USE_GLOBAL_OFFSET; + } + const auto graphics_api = static_cast(index); + is_visible = (using_global && global_graphics_api == Settings::GraphicsAPI::Vulkan) || + graphics_api == Settings::GraphicsAPI::Vulkan; + } else { + const auto graphics_api = static_cast(index); + is_visible = graphics_api == Settings::GraphicsAPI::Vulkan; + } + ui->physical_device_group->setVisible(is_visible); + ui->spirv_shader_gen->setVisible(is_visible); } diff --git a/src/citra_qt/configuration/configure_graphics.h b/src/citra_qt/configuration/configure_graphics.h index 44b12cfa9..97a3fe0aa 100644 --- a/src/citra_qt/configuration/configure_graphics.h +++ b/src/citra_qt/configuration/configure_graphics.h @@ -5,6 +5,8 @@ #pragma once #include +#include +#include #include namespace Ui { @@ -19,7 +21,8 @@ class ConfigureGraphics : public QWidget { Q_OBJECT public: - explicit ConfigureGraphics(bool is_powered_on, QWidget* parent = nullptr); + explicit ConfigureGraphics(std::span physical_devices, bool is_powered_on, + QWidget* parent = nullptr); ~ConfigureGraphics() override; void ApplyConfiguration(); @@ -30,11 +33,15 @@ public: private: void SetupPerGameUI(); + void SetPhysicalDeviceComboVisibility(int index); ConfigurationShared::CheckState use_hw_shader; ConfigurationShared::CheckState shaders_accurate_mul; ConfigurationShared::CheckState use_disk_shader_cache; ConfigurationShared::CheckState use_vsync_new; + ConfigurationShared::CheckState async_shader_compilation; + ConfigurationShared::CheckState async_presentation; + ConfigurationShared::CheckState spirv_shader_gen; std::unique_ptr ui; QColor bg_color; }; diff --git a/src/citra_qt/configuration/configure_graphics.ui b/src/citra_qt/configuration/configure_graphics.ui index 22d799784..bd77e6d6b 100644 --- a/src/citra_qt/configuration/configure_graphics.ui +++ b/src/citra_qt/configuration/configure_graphics.ui @@ -7,7 +7,7 @@ 0 0 400 - 443 + 509 @@ -73,6 +73,41 @@ + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Physical Device + + + + + + + + + + + + + SPIR-V Shader Generation + + + @@ -100,7 +135,7 @@ - <html><head/><body><p>Use OpenGL to accelerate shader emulation.</p><p>Requires a relatively powerful GPU for better performance.</p></body></html> + <html><head/><body><p>Use the selected graphics API to accelerate shader emulation.</p><p>Requires a relatively powerful GPU for better performance.</p></body></html> Enable Hardware Shader @@ -148,6 +183,26 @@ + + + + <html><head/><body><p>Compile shaders using background threads to avoid shader compilation stutter. Expect temporary graphical glitches</p></body></html> + + + Enable Async Shader Compilation + + + + + + + <html><head/><body><p>Perform presentation on separate threads. Improves performance when using Vulkan in most games.</p></body></html> + + + Enable Async Presentation + + + diff --git a/src/citra_qt/configuration/configure_per_game.cpp b/src/citra_qt/configuration/configure_per_game.cpp index a9896e0ad..c5aa3f5ca 100644 --- a/src/citra_qt/configuration/configure_per_game.cpp +++ b/src/citra_qt/configuration/configure_per_game.cpp @@ -24,7 +24,7 @@ #include "ui_configure_per_game.h" ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const QString& file_name, - Core::System& system_) + std::span physical_devices, Core::System& system_) : QDialog(parent), ui(std::make_unique()), filename{file_name.toStdString()}, title_id{title_id_}, system{system_} { const auto config_file_name = title_id == 0 ? std::string(FileUtil::GetFilename(filename)) @@ -35,7 +35,7 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const QString audio_tab = std::make_unique(is_powered_on, this); general_tab = std::make_unique(this); enhancements_tab = std::make_unique(this); - graphics_tab = std::make_unique(is_powered_on, this); + graphics_tab = std::make_unique(physical_devices, is_powered_on, this); system_tab = std::make_unique(system, this); debug_tab = std::make_unique(is_powered_on, this); cheat_tab = std::make_unique(system, title_id, this); diff --git a/src/citra_qt/configuration/configure_per_game.h b/src/citra_qt/configuration/configure_per_game.h index e3953fcb7..ef5ea731d 100644 --- a/src/citra_qt/configuration/configure_per_game.h +++ b/src/citra_qt/configuration/configure_per_game.h @@ -4,9 +4,11 @@ #pragma once #include +#include #include #include #include +#include #include "citra_qt/configuration/config.h" namespace Core { @@ -35,9 +37,8 @@ class ConfigurePerGame : public QDialog { Q_OBJECT public: - // Cannot use std::filesystem::path due to https://bugreports.qt.io/browse/QTBUG-73263 explicit ConfigurePerGame(QWidget* parent, u64 title_id_, const QString& file_name, - Core::System& system_); + std::span physical_devices, Core::System& system_); ~ConfigurePerGame() override; /// Loads all button configurations to settings file diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 560cc23dd..65d88770b 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -62,6 +62,7 @@ #include "citra_qt/uisettings.h" #include "citra_qt/updater/updater.h" #include "citra_qt/util/clickable_label.h" +#include "citra_qt/util/vk_device_info.h" #include "common/arch.h" #include "common/common_paths.h" #include "common/detached_tasks.h" @@ -263,6 +264,14 @@ GMainWindow::GMainWindow(Core::System& system_) connect(&mouse_hide_timer, &QTimer::timeout, this, &GMainWindow::HideMouseCursor); connect(ui->menubar, &QMenuBar::hovered, this, &GMainWindow::OnMouseActivity); + physical_devices = GetVulkanPhysicalDevices(); + if (physical_devices.empty()) { + QMessageBox::warning(this, tr("No Suitable Vulkan Devices Detected"), + tr("Vulkan initialization failed during boot.
" + "Your GPU may not support Vulkan 1.1, or you do not " + "have the latest graphics driver.")); + } + #if ENABLE_QT_UPDATER if (UISettings::values.check_for_update_on_start) { CheckForUpdates(); @@ -2010,7 +2019,7 @@ void GMainWindow::OnLoadState() { void GMainWindow::OnConfigure() { game_list->SetDirectoryWatcherEnabled(false); Settings::SetConfiguringGlobal(true); - ConfigureDialog configureDialog(this, hotkey_registry, system, + ConfigureDialog configureDialog(this, hotkey_registry, system, physical_devices, !multiplayer_state->IsHostingPublicRoom()); connect(&configureDialog, &ConfigureDialog::LanguageChanged, this, &GMainWindow::OnLanguageChanged); @@ -2766,7 +2775,7 @@ void GMainWindow::OnConfigurePerGame() { void GMainWindow::OpenPerGameConfiguration(u64 title_id, const QString& file_name) { Settings::SetConfiguringGlobal(false); - ConfigurePerGame dialog(this, title_id, file_name, system); + ConfigurePerGame dialog(this, title_id, file_name, physical_devices, system); const auto result = dialog.exec(); if (result != QDialog::Accepted) { diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h index d3c4d03dc..c5de701e1 100644 --- a/src/citra_qt/main.h +++ b/src/citra_qt/main.h @@ -6,8 +6,10 @@ #include #include +#include #include #include +#include #include #include #include "citra_qt/compatibility_list.h" @@ -326,6 +328,8 @@ private: // Whether game was paused due to stopping video dumping bool game_paused_for_dumping = false; + std::vector physical_devices; + // Debugger panes ProfilerWidget* profilerWidget; MicroProfileDialog* microProfileDialog; diff --git a/src/citra_qt/util/vk_device_info.cpp b/src/citra_qt/util/vk_device_info.cpp new file mode 100644 index 000000000..276eb93c7 --- /dev/null +++ b/src/citra_qt/util/vk_device_info.cpp @@ -0,0 +1,23 @@ +// Copyright 2023 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "citra_qt/util/vk_device_info.h" +#include "video_core/renderer_vulkan/vk_instance.h" + +std::vector GetVulkanPhysicalDevices() { + std::vector result; + try { + Vulkan::Instance instance{}; + const auto physical_devices = instance.GetPhysicalDevices(); + + for (const vk::PhysicalDevice physical_device : physical_devices) { + const QString name = QString::fromLocal8Bit(physical_device.getProperties().deviceName); + result.push_back(name); + } + } catch (...) { + LOG_ERROR(Frontend, "Error occured while querying for physical devices."); + } + + return result; +} \ No newline at end of file diff --git a/src/citra_qt/util/vk_device_info.h b/src/citra_qt/util/vk_device_info.h new file mode 100644 index 000000000..c8ef6343b --- /dev/null +++ b/src/citra_qt/util/vk_device_info.h @@ -0,0 +1,11 @@ +// Copyright 2023 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +/// Returns a list of all available vulkan GPUs. +std::vector GetVulkanPhysicalDevices(); \ No newline at end of file