diff options
| -rw-r--r-- | src/yuzu/configuration/configure_graphics.cpp | 155 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_graphics.h | 10 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_graphics.ui | 23 | 
3 files changed, 161 insertions, 27 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 <vector>  #include <QColorDialog> +#include <QGuiApplication>  #include <QVulkanInstance> +#include <QWindow> +#include <vulkan/vulkan_core.h> +#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<VkPresentModeKHR> 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<Ui::ConfigureGraphics>()}, 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<int>(&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<int>(&QComboBox::activated), this, -            [this](int device) { UpdateDeviceSelection(device); }); +    connect(ui->device, qOverload<int>(&QComboBox::activated), this, [this](int device) { +        UpdateDeviceSelection(device); +        PopulateVSyncModeSelection(); +    });      connect(ui->backend, qOverload<int>(&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<int>(Settings::values.anti_aliasing.GetValue())); - -        ui->vsync_mode_combobox->setCurrentIndex( -            static_cast<int>(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<unsigned long>(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<Settings::ResolutionSetup>(          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<Settings::VSyncMode>(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<int>(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<VkPhysicalDevice> 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<VkPresentModeKHR> 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 <vector>  #include <QString>  #include <QWidget> +#include <vulkan/vulkan_core.h>  #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<QString> vulkan_devices; +    std::vector<std::vector<VkPresentModeKHR>> device_present_modes; +    std::vector<VkPresentModeKHR> +        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 @@             </item>             <item>              <widget class="QComboBox" name="vsync_mode_combobox"> +             <property name="toolTip"> +              <string>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.</string> +             </property>               <property name="currentText"> -              <string>Off (Immediate)</string> +              <string/>               </property> -             <item> -              <property name="text"> -               <string>Off (Immediate)</string> -              </property> -             </item> -             <item> -              <property name="text"> -               <string>Double Buffering (FIFO)</string> -              </property> -             </item> -             <item> -              <property name="text"> -               <string>Triple Buffering (Mailbox)</string> -              </property> -             </item>              </widget>             </item>            </layout> | 
