citra_qt: Add vulkan options to the GUI

This commit is contained in:
GPUCode 2023-07-25 14:04:08 +03:00
parent a67bfe544d
commit 6b51afaf1f
14 changed files with 256 additions and 24 deletions

View file

@ -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

View file

@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <QDesktopServices>
#include <QMessageBox>
#include <QUrl>
#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 "
"<strong>VK_LAYER_KHRONOS_validation</strong> 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 "
"<strong>VK_LAYER_LUNARG_api_dump</strong> 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,

View file

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>523</width>
<height>447</height>
<height>458</height>
</rect>
</property>
<property name="windowTitle">
@ -112,16 +112,6 @@
<string>CPU</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="2" column="0">
<widget class="QCheckBox" name="toggle_cpu_jit">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enables the use of the ARM JIT compiler for emulating the 3DS CPUs. Don't disable unless for debugging purposes&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Enable CPU JIT</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QWidget" name="clock_speed_widget" native="true">
<layout class="QHBoxLayout" name="clock_speed_layout">
@ -202,6 +192,16 @@
</layout>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="toggle_cpu_jit">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enables the use of the ARM JIT compiler for emulating the 3DS CPUs. Don't disable unless for debugging purposes&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Enable CPU JIT</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="toggle_renderer_debug">
<property name="text">
@ -209,6 +209,13 @@
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="toggle_dump_command_buffers">
<property name="text">
<string>Dump command buffers</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>

View file

@ -23,14 +23,14 @@
#include "ui_configure.h"
ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, Core::System& system_,
bool enable_web_config)
std::span<const QString> physical_devices, bool enable_web_config)
: QDialog(parent), ui{std::make_unique<Ui::ConfigureDialog>()}, registry{registry_},
system{system_}, is_powered_on{system.IsPoweredOn()},
general_tab{std::make_unique<ConfigureGeneral>(this)},
system_tab{std::make_unique<ConfigureSystem>(system, this)},
input_tab{std::make_unique<ConfigureInput>(this)},
hotkeys_tab{std::make_unique<ConfigureHotkeys>(this)},
graphics_tab{std::make_unique<ConfigureGraphics>(is_powered_on, this)},
graphics_tab{std::make_unique<ConfigureGraphics>(physical_devices, is_powered_on, this)},
enhancements_tab{std::make_unique<ConfigureEnhancements>(this)},
audio_tab{std::make_unique<ConfigureAudio>(is_powered_on, this)},
camera_tab{std::make_unique<ConfigureCamera>(this)},

View file

@ -5,7 +5,9 @@
#pragma once
#include <memory>
#include <span>
#include <QDialog>
#include <QString>
class HotkeyRegistry;
@ -35,6 +37,7 @@ class ConfigureDialog : public QDialog {
public:
explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry, Core::System& system,
std::span<const QString> physical_devices,
bool enable_web_config = true);
~ConfigureDialog() override;

View file

@ -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<const QString> physical_devices, bool is_powered_on,
QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureGraphics>()) {
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<u32>(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<int>(&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<int>(&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<int>(Settings::values.graphics_api.GetValue()));
ui->physical_device_combo->setCurrentIndex(
static_cast<int>(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<u32>(Settings::values.graphics_api.GetValue(true)));
ConfigurationShared::SetColoredComboBox(
ui->physical_device_combo, ui->physical_device_group,
static_cast<u32>(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<Settings::GraphicsAPI>(index);
is_visible = (using_global && global_graphics_api == Settings::GraphicsAPI::Vulkan) ||
graphics_api == Settings::GraphicsAPI::Vulkan;
} else {
const auto graphics_api = static_cast<Settings::GraphicsAPI>(index);
is_visible = graphics_api == Settings::GraphicsAPI::Vulkan;
}
ui->physical_device_group->setVisible(is_visible);
ui->spirv_shader_gen->setVisible(is_visible);
}

View file

@ -5,6 +5,8 @@
#pragma once
#include <memory>
#include <span>
#include <QString>
#include <QWidget>
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<const QString> 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::ConfigureGraphics> ui;
QColor bg_color;
};

View file

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>400</width>
<height>443</height>
<height>509</height>
</rect>
</property>
<property name="minimumSize">
@ -73,6 +73,41 @@
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="physical_device_group" native="true">
<layout class="QHBoxLayout" name="physical_device_group_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="physical_device_label">
<property name="text">
<string>Physical Device</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="physical_device_combo"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QCheckBox" name="spirv_shader_gen">
<property name="text">
<string>SPIR-V Shader Generation</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@ -100,7 +135,7 @@
<item>
<widget class="QCheckBox" name="toggle_hw_shader">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Use OpenGL to accelerate shader emulation.&lt;/p&gt;&lt;p&gt;Requires a relatively powerful GPU for better performance.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Use the selected graphics API to accelerate shader emulation.&lt;/p&gt;&lt;p&gt;Requires a relatively powerful GPU for better performance.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Enable Hardware Shader</string>
@ -148,6 +183,26 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="toggle_async_shaders">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Compile shaders using background threads to avoid shader compilation stutter. Expect temporary graphical glitches&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Enable Async Shader Compilation</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="toggle_async_present">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Perform presentation on separate threads. Improves performance when using Vulkan in most games.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Enable Async Presentation</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>

View file

@ -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<const QString> physical_devices, Core::System& system_)
: QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGame>()),
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<ConfigureAudio>(is_powered_on, this);
general_tab = std::make_unique<ConfigureGeneral>(this);
enhancements_tab = std::make_unique<ConfigureEnhancements>(this);
graphics_tab = std::make_unique<ConfigureGraphics>(is_powered_on, this);
graphics_tab = std::make_unique<ConfigureGraphics>(physical_devices, is_powered_on, this);
system_tab = std::make_unique<ConfigureSystem>(system, this);
debug_tab = std::make_unique<ConfigureDebug>(is_powered_on, this);
cheat_tab = std::make_unique<ConfigureCheats>(system, title_id, this);

View file

@ -4,9 +4,11 @@
#pragma once
#include <memory>
#include <span>
#include <string>
#include <QDialog>
#include <QList>
#include <QString>
#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<const QString> physical_devices, Core::System& system_);
~ConfigurePerGame() override;
/// Loads all button configurations to settings file

View file

@ -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.<br/>"
"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) {

View file

@ -6,8 +6,10 @@
#include <array>
#include <memory>
#include <vector>
#include <QMainWindow>
#include <QPushButton>
#include <QString>
#include <QTimer>
#include <QTranslator>
#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<QString> physical_devices;
// Debugger panes
ProfilerWidget* profilerWidget;
MicroProfileDialog* microProfileDialog;

View file

@ -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<QString> GetVulkanPhysicalDevices() {
std::vector<QString> 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;
}

View file

@ -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 <vector>
#include <QString>
/// Returns a list of all available vulkan GPUs.
std::vector<QString> GetVulkanPhysicalDevices();