From a546ecbb1241a28b808d3a9ebe1a55f001c267ed Mon Sep 17 00:00:00 2001 From: lat9nq <22451773+lat9nq@users.noreply.github.com> Date: Mon, 1 May 2023 20:36:23 -0400 Subject: [PATCH] configure_graphics: Actively find present modes When Vulkan devices are enumerated, this also determines the available present modes for each device, maps them to a vector, and gives those options to the user. OpenGL options are limited to On/Off. Required creating a VkSurfaceKHR during device enumeration, which may or may not be desireable. For the sake of a less confusing UI. Also fixes a bug where if a graphics device disappears on the host, we don't try and select the non-existant devices. configure_graphics: Remove vsync runtime lock for Vulkan configure_graphics: Recommend Mailbox present mode configure_graphics: Fix type-limits warning configure_graphics: Clean up includes configure_graphics: Add tooltip --- src/yuzu/configuration/configure_graphics.cpp | 155 ++++++++++++++++-- src/yuzu/configuration/configure_graphics.h | 10 ++ src/yuzu/configuration/configure_graphics.ui | 25 +-- 3 files changed, 162 insertions(+), 28 deletions(-) diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index 17a54f0f4..3e3398f42 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp @@ -4,20 +4,62 @@ // Include this early to include Vulkan headers how we want to #include "video_core/vulkan_common/vulkan_wrapper.h" +#include #include +#include #include +#include +#include +#include "bootmanager.h" #include "common/common_types.h" #include "common/logging/log.h" #include "common/settings.h" #include "core/core.h" +#include "core/frontend/emu_window.h" #include "ui_configure_graphics.h" #include "video_core/vulkan_common/vulkan_instance.h" #include "video_core/vulkan_common/vulkan_library.h" +#include "video_core/vulkan_common/vulkan_surface.h" #include "yuzu/configuration/configuration_shared.h" #include "yuzu/configuration/configure_graphics.h" +#include "yuzu/qt_common.h" #include "yuzu/uisettings.h" +static const std::vector default_present_modes{VK_PRESENT_MODE_IMMEDIATE_KHR, + VK_PRESENT_MODE_FIFO_KHR}; + +// Converts a setting to a present mode (or vice versa) +static constexpr VkPresentModeKHR VSyncSettingToMode(Settings::VSyncMode mode) { + switch (mode) { + case Settings::VSyncMode::Immediate: + return VK_PRESENT_MODE_IMMEDIATE_KHR; + case Settings::VSyncMode::Mailbox: + return VK_PRESENT_MODE_MAILBOX_KHR; + case Settings::VSyncMode::FIFO: + return VK_PRESENT_MODE_FIFO_KHR; + case Settings::VSyncMode::FIFORelaxed: + return VK_PRESENT_MODE_FIFO_RELAXED_KHR; + default: + return VK_PRESENT_MODE_FIFO_KHR; + } +} + +static constexpr Settings::VSyncMode PresentModeToSetting(VkPresentModeKHR mode) { + switch (mode) { + case VK_PRESENT_MODE_IMMEDIATE_KHR: + return Settings::VSyncMode::Immediate; + case VK_PRESENT_MODE_MAILBOX_KHR: + return Settings::VSyncMode::Mailbox; + case VK_PRESENT_MODE_FIFO_KHR: + return Settings::VSyncMode::FIFO; + case VK_PRESENT_MODE_FIFO_RELAXED_KHR: + return Settings::VSyncMode::FIFORelaxed; + default: + return Settings::VSyncMode::FIFO; + } +} + ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* parent) : QWidget(parent), ui{std::make_unique()}, system{system_} { vulkan_device = Settings::values.vulkan_device.GetValue(); @@ -39,13 +81,16 @@ ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* paren connect(ui->api, qOverload(&QComboBox::currentIndexChanged), this, [this] { UpdateAPILayout(); + PopulateVSyncModeSelection(); if (!Settings::IsConfiguringGlobal()) { ConfigurationShared::SetHighlight( ui->api_widget, ui->api->currentIndex() != ConfigurationShared::USE_GLOBAL_INDEX); } }); - connect(ui->device, qOverload(&QComboBox::activated), this, - [this](int device) { UpdateDeviceSelection(device); }); + connect(ui->device, qOverload(&QComboBox::activated), this, [this](int device) { + UpdateDeviceSelection(device); + PopulateVSyncModeSelection(); + }); connect(ui->backend, qOverload(&QComboBox::activated), this, [this](int backend) { UpdateShaderBackendSelection(backend); }); @@ -70,6 +115,43 @@ ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* paren ui->fsr_sharpening_label->setVisible(Settings::IsConfiguringGlobal()); } +void ConfigureGraphics::PopulateVSyncModeSelection() { + const Settings::RendererBackend backend{GetCurrentGraphicsBackend()}; + if (backend == Settings::RendererBackend::Null) { + ui->vsync_mode_combobox->setEnabled(false); + return; + } + ui->vsync_mode_combobox->setEnabled(true); + + const int current_index = //< current selected vsync mode from combobox + ui->vsync_mode_combobox->currentIndex(); + const auto current_mode = //< current selected vsync mode as a VkPresentModeKHR + current_index == -1 ? VSyncSettingToMode(Settings::values.vsync_mode.GetValue()) + : vsync_mode_combobox_enum_map[current_index]; + int index{}; + const int device{ui->device->currentIndex()}; //< current selected Vulkan device + const auto& present_modes = //< relevant vector of present modes for the selected device or API + backend == Settings::RendererBackend::Vulkan ? device_present_modes[device] + : default_present_modes; + + ui->vsync_mode_combobox->clear(); + vsync_mode_combobox_enum_map.clear(); + vsync_mode_combobox_enum_map.reserve(present_modes.size()); + for (const auto present_mode : present_modes) { + const auto mode_name = TranslateVSyncMode(present_mode, backend); + if (mode_name.isEmpty()) { + continue; + } + + ui->vsync_mode_combobox->insertItem(index, mode_name); + vsync_mode_combobox_enum_map.push_back(present_mode); + if (present_mode == current_mode) { + ui->vsync_mode_combobox->setCurrentIndex(index); + } + index++; + } +} + void ConfigureGraphics::UpdateDeviceSelection(int device) { if (device == -1) { return; @@ -99,7 +181,9 @@ void ConfigureGraphics::SetConfiguration() { ui->nvdec_emulation_widget->setEnabled(runtime_lock); ui->resolution_combobox->setEnabled(runtime_lock); ui->accelerate_astc->setEnabled(runtime_lock); - ui->vsync_mode_combobox->setEnabled(runtime_lock); + ui->vsync_mode_layout->setEnabled(runtime_lock || + Settings::values.renderer_backend.GetValue() == + Settings::RendererBackend::Vulkan); ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache.GetValue()); ui->use_asynchronous_gpu_emulation->setChecked( Settings::values.use_asynchronous_gpu_emulation.GetValue()); @@ -119,9 +203,6 @@ void ConfigureGraphics::SetConfiguration() { ui->fsr_sharpening_slider->setValue(Settings::values.fsr_sharpening_slider.GetValue()); ui->anti_aliasing_combobox->setCurrentIndex( static_cast(Settings::values.anti_aliasing.GetValue())); - - ui->vsync_mode_combobox->setCurrentIndex( - static_cast(Settings::values.vsync_mode.GetValue())); } else { ConfigurationShared::SetPerGameSetting(ui->api, &Settings::values.renderer_backend); ConfigurationShared::SetHighlight(ui->api_widget, @@ -174,7 +255,24 @@ void ConfigureGraphics::SetConfiguration() { Settings::values.bg_green.GetValue(), Settings::values.bg_blue.GetValue())); UpdateAPILayout(); + PopulateVSyncModeSelection(); //< must happen after UpdateAPILayout SetFSRIndicatorText(ui->fsr_sharpening_slider->sliderPosition()); + + // V-Sync setting needs to be determined after populating the V-Sync combobox + if (Settings::IsConfiguringGlobal()) { + const auto vsync_mode_setting = Settings::values.vsync_mode.GetValue(); + const auto vsync_mode = VSyncSettingToMode(vsync_mode_setting); + int index{}; + for (const auto mode : vsync_mode_combobox_enum_map) { + if (mode == vsync_mode) { + break; + } + index++; + } + if (static_cast(index) < vsync_mode_combobox_enum_map.size()) { + ui->vsync_mode_combobox->setCurrentIndex(index); + } + } } void ConfigureGraphics::SetFSRIndicatorText(int percentage) { @@ -182,6 +280,27 @@ void ConfigureGraphics::SetFSRIndicatorText(int percentage) { tr("%1%", "FSR sharpening percentage (e.g. 50%)").arg(100 - (percentage / 2))); } +const QString ConfigureGraphics::TranslateVSyncMode(VkPresentModeKHR mode, + Settings::RendererBackend backend) const { + switch (mode) { + case VK_PRESENT_MODE_IMMEDIATE_KHR: + return backend == Settings::RendererBackend::OpenGL + ? tr("Off") + : QStringLiteral("Immediate (%1)").arg(tr("V-Sync Off")); + case VK_PRESENT_MODE_MAILBOX_KHR: + return QStringLiteral("Mailbox (%1)").arg(tr("Recommended")); + case VK_PRESENT_MODE_FIFO_KHR: + return backend == Settings::RendererBackend::OpenGL + ? tr("On") + : QStringLiteral("FIFO (%1)").arg(tr("V-Sync On")); + case VK_PRESENT_MODE_FIFO_RELAXED_KHR: + return QStringLiteral("FIFO Relaxed"); + default: + return {}; + break; + } +} + void ConfigureGraphics::ApplyConfiguration() { const auto resolution_setup = static_cast( ui->resolution_combobox->currentIndex() - @@ -237,8 +356,9 @@ void ConfigureGraphics::ApplyConfiguration() { } Settings::values.fsr_sharpening_slider.SetValue(ui->fsr_sharpening_slider->value()); - Settings::values.vsync_mode.SetValue( - static_cast(ui->vsync_mode_combobox->currentIndex())); + const auto mode = vsync_mode_combobox_enum_map[ui->vsync_mode_combobox->currentIndex()]; + const auto vsync_mode = PresentModeToSetting(mode); + Settings::values.vsync_mode.SetValue(vsync_mode); } else { if (ui->resolution_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { Settings::values.resolution_setup.SetGlobal(true); @@ -352,7 +472,9 @@ void ConfigureGraphics::UpdateAPILayout() { ui->backend_widget->setVisible(true); break; case Settings::RendererBackend::Vulkan: - ui->device->setCurrentIndex(vulkan_device); + if (static_cast(vulkan_device) < ui->device->count()) { + ui->device->setCurrentIndex(vulkan_device); + } ui->device_widget->setVisible(true); ui->backend_widget->setVisible(false); break; @@ -370,16 +492,27 @@ void ConfigureGraphics::RetrieveVulkanDevices() try { using namespace Vulkan; + auto* window = this->window()->windowHandle(); + auto wsi = YuzuQtCommon::GetWindowSystemInfo(window); + vk::InstanceDispatch dld; const Common::DynamicLibrary library = OpenLibrary(); - const vk::Instance instance = CreateInstance(library, dld, VK_API_VERSION_1_1); + const vk::Instance instance = CreateInstance(library, dld, VK_API_VERSION_1_1, wsi.type); const std::vector physical_devices = instance.EnumeratePhysicalDevices(); + vk::SurfaceKHR surface = //< needed to view present modes for a device + CreateSurface(instance, wsi); vulkan_devices.clear(); vulkan_devices.reserve(physical_devices.size()); + device_present_modes.clear(); + device_present_modes.reserve(physical_devices.size()); for (const VkPhysicalDevice device : physical_devices) { - const std::string name = vk::PhysicalDevice(device, dld).GetProperties().deviceName; + const auto physical_device = vk::PhysicalDevice(device, dld); + const std::string name = physical_device.GetProperties().deviceName; + const std::vector present_modes = + physical_device.GetSurfacePresentModesKHR(*surface); vulkan_devices.push_back(QString::fromStdString(name)); + device_present_modes.push_back(present_modes); } } catch (const Vulkan::vk::Exception& exception) { LOG_ERROR(Frontend, "Failed to enumerate devices with error: {}", exception.what()); diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h index d98d6624e..6d8002de2 100644 --- a/src/yuzu/configuration/configure_graphics.h +++ b/src/yuzu/configuration/configure_graphics.h @@ -7,6 +7,7 @@ #include #include #include +#include #include "common/settings.h" namespace Core { @@ -35,6 +36,7 @@ private: void changeEvent(QEvent* event) override; void RetranslateUI(); + void PopulateVSyncModeSelection(); void UpdateBackgroundColorButton(QColor color); void UpdateAPILayout(); void UpdateDeviceSelection(int device); @@ -43,6 +45,10 @@ private: void RetrieveVulkanDevices(); void SetFSRIndicatorText(int percentage); + /* Turns a Vulkan present mode into a textual string for a UI + * (and eventually for a human to read) */ + const QString TranslateVSyncMode(VkPresentModeKHR mode, + Settings::RendererBackend backend) const; void SetupPerGameUI(); @@ -58,6 +64,10 @@ private: ConfigurationShared::CheckState use_asynchronous_gpu_emulation; std::vector vulkan_devices; + std::vector> device_present_modes; + std::vector + vsync_mode_combobox_enum_map; //< Keeps track of which present mode corresponds to which + // selection in the combobox u32 vulkan_device{}; Settings::ShaderBackend shader_backend{}; diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui index 9ad1c88a7..94eba6c7e 100644 --- a/src/yuzu/configuration/configure_graphics.ui +++ b/src/yuzu/configuration/configure_graphics.ui @@ -212,24 +212,15 @@ - - Off (Immediate) + + FIFO (V-Sync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. +FIFO Relaxed is similar to FIFO but allows tearing as it recovers from a slow down. +Mailbox can have lower latency than FIFO and does not tear but may drop frames. +Immediate (no synchronization) just presents whatever is available and can exhibit tearing. + + + - - - Off (Immediate) - - - - - Double Buffering (FIFO) - - - - - Triple Buffering (Mailbox) - -